Loading libraries

library(GEOquery)
Loading required package: Biobase
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename,
    cbind, colnames, dirname, do.call, duplicated, eval,
    evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, Map, mapply, match, mget,
    order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply,
    setdiff, sort, table, tapply, union, unique,
    unsplit, which.max, which.min

Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages
    'citation("pkgname")'.
library(oligo)
Loading required package: oligoClasses
Welcome to oligoClasses version 1.56.0
Loading required package: Biostrings
Loading required package: S4Vectors
Loading required package: stats4

Attaching package: ‘S4Vectors’

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges
Loading required package: XVector
Loading required package: GenomeInfoDb

Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit

================================================================
Welcome to oligo version 1.58.0
================================================================
library(sva)
Loading required package: mgcv
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:Biostrings’:

    collapse

The following object is masked from ‘package:IRanges’:

    collapse

This is mgcv 1.8-38. For overview type 'help("mgcv-package")'.
Loading required package: genefilter
Loading required package: BiocParallel
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.0     ✓ forcats 0.5.1
── Conflicts ────────────────────────── tidyverse_conflicts() ──
x dplyr::collapse()   masks nlme::collapse(), Biostrings::collapse(), IRanges::collapse()
x dplyr::combine()    masks Biobase::combine(), BiocGenerics::combine()
x purrr::compact()    masks XVector::compact()
x dplyr::desc()       masks IRanges::desc()
x tidyr::expand()     masks S4Vectors::expand()
x dplyr::filter()     masks stats::filter()
x dplyr::first()      masks S4Vectors::first()
x dplyr::lag()        masks stats::lag()
x ggplot2::Position() masks BiocGenerics::Position(), base::Position()
x purrr::reduce()     masks IRanges::reduce()
x dplyr::rename()     masks S4Vectors::rename()
x dplyr::slice()      masks XVector::slice(), IRanges::slice()
x readr::spec()       masks genefilter::spec()
x dplyr::summarize()  masks oligo::summarize()
library(ggsci)
library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(pheatmap)
library(dendextend)

---------------------
Welcome to dendextend version 1.15.2
Type citation('dendextend') for how to cite the package.

Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/

Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
You may ask questions at stackoverflow, use the r and dendextend tags: 
     https://stackoverflow.com/questions/tagged/dendextend

    To suppress this message use:  suppressPackageStartupMessages(library(dendextend))
---------------------


Attaching package: ‘dendextend’

The following object is masked from ‘package:Biostrings’:

    nnodes

The following object is masked from ‘package:stats’:

    cutree
library(caret)
Loading required package: lattice

Attaching package: ‘caret’

The following object is masked from ‘package:purrr’:

    lift
library(RColorBrewer)
library(viridis)
Loading required package: viridisLite
library(UpSetR)

Attaching package: ‘UpSetR’

The following object is masked from ‘package:lattice’:

    histogram
library(ComplexUpset)

Attaching package: ‘ComplexUpset’

The following object is masked from ‘package:UpSetR’:

    upset

Custom functions

# given a matrix, perform min-max scaling on its columns
min_max_mat <- function(mat){
  mat_rescaled <- apply(mat, 2, function(v){
    v_range <- range(v)
    names(v_range) <- c("minimum", "maximum")
    range_difference <- v_range["maximum"] - v_range["minimum"]
    rescaled <- (v - v_range["minimum"])/range_difference
    return(rescaled)
  })
  return(mat_rescaled)
}

Getting data from GEOquery

# geodata <- GEOquery::getGEO(GEO = "GSE76275", destdir = "./tempfiles")
# geodata <- GEOquery::getGEO(filename = "./tempfiles/GSE76275_series_matrix.txt.gz")
# saveRDS(geodata, "geodata.RDS")
geodata <- readRDS("geodata.RDS")
# mdata <- geodata %>% 
#   pluck(1) %>% 
#   phenoData() %>%
#   pData() %>% as_tibble()
# feature_data <- geodata %>% 
#   pluck(1) %>% 
#   featureData()
  
# write_csv(mdata, "raw_mdata.csv")
mdata <- read_csv("raw_mdata.csv")
Rows: 265 Columns: 69
── Column specification ────────────────────────────────────────
Delimiter: ","
chr (62): title, geo_accession, status, submission_date, las...
dbl  (6): channel_count, taxid_ch1, contact_zip/postal_code,...
lgl  (1): growth_protocol_ch1

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# saveRDS(feature_data, "featureData.RDS")
# feature_data <- readRDS("featureData.RDS")

Inspecting and cleaning the metadata

mdata %>% 
  glimpse()
Rows: 265
Columns: 23
$ title                                <chr> "S1-H10", "S1-H14…
$ submission_date                      <chr> "Dec 17 2015", "D…
$ last_update_date                     <chr> "Dec 18 2015", "D…
$ geo_accession                        <chr> "GSM1974566", "GS…
$ `age (years):ch1`                    <dbl> NA, 41, 55, 55, 6…
$ `ajcc stage (7th edition, 2010):ch1` <chr> "T2N1M0", "T1N0M0…
$ `body mass index:ch1`                <dbl> 32, 29, NA, 31, 3…
$ `er:ch1`                             <chr> "Negative", "Nega…
$ `gender:ch1`                         <chr> "Female", "Female…
$ `her2:ch1`                           <chr> "Negative", "Nega…
$ `histology group:ch1`                <chr> "Infiltrating Duc…
$ `histology:ch1`                      <chr> "Infiltrating Duc…
$ `menopausal status:ch1`              <chr> "Post-Menopausal"…
$ `metastases:ch1`                     <chr> "No mets", "No me…
$ `positive nodes:ch1`                 <chr> "1 - 3", "0", "0"…
$ `pr:ch1`                             <chr> "Negative", "Nega…
$ `race:ch1`                           <chr> "Caucasian", "Cau…
$ `set:ch1`                            <chr> "Validation TN", …
$ `tissue:ch1`                         <chr> "Breast cancer", …
$ `tnbc subtype:ch1`                   <chr> "Mesenchymal (MES…
$ `triple-negative status:ch1`         <chr> "TN", "TN", "TN",…
$ `tumor grade:ch1`                    <chr> NA, "Poorly Diffe…
$ `tumor size:ch1`                     <chr> "2 - 5 cm", "<=2c…
mdata <- mdata %>% 
  select(title, contains("date"), geo_accession, contains(":ch1"))

colnames(mdata)
 [1] "title"                             
 [2] "submission_date"                   
 [3] "last_update_date"                  
 [4] "geo_accession"                     
 [5] "age (years):ch1"                   
 [6] "ajcc stage (7th edition, 2010):ch1"
 [7] "body mass index:ch1"               
 [8] "er:ch1"                            
 [9] "gender:ch1"                        
[10] "her2:ch1"                          
[11] "histology group:ch1"               
[12] "histology:ch1"                     
[13] "menopausal status:ch1"             
[14] "metastases:ch1"                    
[15] "positive nodes:ch1"                
[16] "pr:ch1"                            
[17] "race:ch1"                          
[18] "set:ch1"                           
[19] "tissue:ch1"                        
[20] "tnbc subtype:ch1"                  
[21] "triple-negative status:ch1"        
[22] "tumor grade:ch1"                   
[23] "tumor size:ch1"                    
  
cnames <- colnames(mdata)
cnames_processed <- str_split(cnames, pattern = ":") %>% 
  map_chr(~{.x[[1]]}) %>% 
  str_replace_all(" ", "_") %>% 
  str_replace_all("-", "_") %>% 
  str_remove_all("\\(|\\)|,")

cnames_processed
 [1] "title"                       "submission_date"            
 [3] "last_update_date"            "geo_accession"              
 [5] "age_years"                   "ajcc_stage_7th_edition_2010"
 [7] "body_mass_index"             "er"                         
 [9] "gender"                      "her2"                       
[11] "histology_group"             "histology"                  
[13] "menopausal_status"           "metastases"                 
[15] "positive_nodes"              "pr"                         
[17] "race"                        "set"                        
[19] "tissue"                      "tnbc_subtype"               
[21] "triple_negative_status"      "tumor_grade"                
[23] "tumor_size"                 
colnames(mdata) <- cnames_processed
rm(cnames, cnames_processed)
glimpse(mdata)
Rows: 265
Columns: 23
$ title                       <chr> "S1-H10", "S1-H14", "S1-H1…
$ submission_date             <chr> "Dec 17 2015", "Dec 17 201…
$ last_update_date            <chr> "Dec 18 2015", "Dec 18 201…
$ geo_accession               <chr> "GSM1974566", "GSM1974567"…
$ age_years                   <dbl> NA, 41, 55, 55, 65, 40, 66…
$ ajcc_stage_7th_edition_2010 <chr> "T2N1M0", "T1N0M0", "T2N0M…
$ body_mass_index             <dbl> 32, 29, NA, 31, 38, 22, 22…
$ er                          <chr> "Negative", "Negative", "N…
$ gender                      <chr> "Female", "Female", "Femal…
$ her2                        <chr> "Negative", "Negative", "N…
$ histology_group             <chr> "Infiltrating Ductal Carci…
$ histology                   <chr> "Infiltrating Ductal Carci…
$ menopausal_status           <chr> "Post-Menopausal", "Post-M…
$ metastases                  <chr> "No mets", "No mets", "No …
$ positive_nodes              <chr> "1 - 3", "0", "0", "0", "4…
$ pr                          <chr> "Negative", "Negative", "N…
$ race                        <chr> "Caucasian", "Caucasian", …
$ set                         <chr> "Validation TN", "Validati…
$ tissue                      <chr> "Breast cancer", "Breast c…
$ tnbc_subtype                <chr> "Mesenchymal (MES)", "Basa…
$ triple_negative_status      <chr> "TN", "TN", "TN", "TN", "T…
$ tumor_grade                 <chr> NA, "Poorly Differentiated…
$ tumor_size                  <chr> "2 - 5 cm", "<=2cm", "2 - …
mdata <- mdata %>% 
  mutate(her2 = if_else(!is.na(her2), her2, "Not Available")) %>% 
  mutate(er = factor(er, levels = c("Negative", "Positive")), 
         pr = factor(pr, levels = c("Negative", "Positive")), 
         her2 = factor(her2, levels = c("Negative", "Positive", "Not Available"))) %>% 
  select(geo_accession, everything())
head(mdata) 

Reading in raw probe intensity data

Celfiles downloaded from GEO and kept the folder celfiles/

celFiles <- list.celfiles('celfiles/', full.names = TRUE, listGzipped = TRUE)
celFiles %>% head()
[1] "celfiles//GSM1974566_S1_H10.CEL.gz" 
[2] "celfiles//GSM1974567_S1_H14.CEL.gz" 
[3] "celfiles//GSM1974568_S1_H19.CEL.gz" 
[4] "celfiles//GSM1974569_S1_H20B.CEL.gz"
[5] "celfiles//GSM1974570_S1_H22.CEL.gz" 
[6] "celfiles//GSM1974571_S1_H27.CEL.gz" 
names(celFiles) <- celFiles %>% 
  basename() %>% 
  str_split("\\.") %>% 
  map_chr(~{.x[1]}) %>% 
  str_split("_") %>% 
  map_chr(~{.x[1]}) 

head(celFiles)
                           GSM1974566 
 "celfiles//GSM1974566_S1_H10.CEL.gz" 
                           GSM1974567 
 "celfiles//GSM1974567_S1_H14.CEL.gz" 
                           GSM1974568 
 "celfiles//GSM1974568_S1_H19.CEL.gz" 
                           GSM1974569 
"celfiles//GSM1974569_S1_H20B.CEL.gz" 
                           GSM1974570 
 "celfiles//GSM1974570_S1_H22.CEL.gz" 
                           GSM1974571 
 "celfiles//GSM1974571_S1_H27.CEL.gz" 

Rearranging rows of metadata to match order of samples in celFiles.

mdata <- mdata[match(mdata$geo_accession, names(celFiles)), ]

Getting only the relevant variables from the metadata.

mdata_subset <- mdata %>%
  select(geo_accession, 
         title, 
         triple_negative_status, 
         tnbc_subtype,
         submission_date,
         er,
         her2,
         pr,
         race,
         set,
         gender, 
         age_years) %>% 
  mutate(across(where(is.character), .fns = factor)) %>% 
  mutate(tnbc_subtype = if_else(is.na(as.character(tnbc_subtype)), "Not Applicable", as.character(tnbc_subtype))) %>% 
  mutate(tnbc_subtype = factor(tnbc_subtype)) %>% 
  as.data.frame()


rownames(mdata_subset) <- as.character(mdata_subset$geo_accession)

head(mdata_subset)
NA
rawData <- read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset))
Platform design info loaded.
Reading in : celfiles//GSM1974566_S1_H10.CEL.gz
Reading in : celfiles//GSM1974567_S1_H14.CEL.gz
Reading in : celfiles//GSM1974568_S1_H19.CEL.gz
Reading in : celfiles//GSM1974569_S1_H20B.CEL.gz
Reading in : celfiles//GSM1974570_S1_H22.CEL.gz
Reading in : celfiles//GSM1974571_S1_H27.CEL.gz
Reading in : celfiles//GSM1974572_S1_H28.CEL.gz
Reading in : celfiles//GSM1974573_S1_H29.CEL.gz
Reading in : celfiles//GSM1974574_S1_H2B.CEL.gz
Reading in : celfiles//GSM1974575_S1_H31.CEL.gz
Reading in : celfiles//GSM1974576_S1_H35B.CEL.gz
Reading in : celfiles//GSM1974577_S1_H36.CEL.gz
Reading in : celfiles//GSM1974578_S1_H38.CEL.gz
Reading in : celfiles//GSM1974579_S1_H3B.CEL.gz
Reading in : celfiles//GSM1974580_S1_H40.CEL.gz
Reading in : celfiles//GSM1974581_S1_H41.CEL.gz
Reading in : celfiles//GSM1974582_S2_H43.CEL.gz
Reading in : celfiles//GSM1974583_S2_H44.CEL.gz
Reading in : celfiles//GSM1974584_S2_H45.CEL.gz
Reading in : celfiles//GSM1974585_S2_H46.CEL.gz
Reading in : celfiles//GSM1974586_S2_H47.CEL.gz
Reading in : celfiles//GSM1974587_S2_H48.CEL.gz
Reading in : celfiles//GSM1974588_S2_H49.CEL.gz
Reading in : celfiles//GSM1974589_S1_H4B.CEL.gz
Reading in : celfiles//GSM1974590_S2_H50.CEL.gz
Reading in : celfiles//GSM1974591_S2_H51.CEL.gz
Reading in : celfiles//GSM1974592_S2_H52.CEL.gz
Reading in : celfiles//GSM1974593_S2_H53.CEL.gz
Reading in : celfiles//GSM1974594_S2_H58B.CEL.gz
Reading in : celfiles//GSM1974595_S2_H59.CEL.gz
Reading in : celfiles//GSM1974596_S1_H6.CEL.gz
Reading in : celfiles//GSM1974597_S2_H60.CEL.gz
Reading in : celfiles//GSM1974598_S2_H61.CEL.gz
Reading in : celfiles//GSM1974599_S2_H62.CEL.gz
Reading in : celfiles//GSM1974600_S2_H63.CEL.gz
Reading in : celfiles//GSM1974601_S2_H64.CEL.gz
Reading in : celfiles//GSM1974602_S2_H65.CEL.gz
Reading in : celfiles//GSM1974603_S2_H66.CEL.gz
Reading in : celfiles//GSM1974604_S2_H67.CEL.gz
Reading in : celfiles//GSM1974605_S2_H68.CEL.gz
Reading in : celfiles//GSM1974606_S2_H69.CEL.gz
Reading in : celfiles//GSM1974607_S1_H7.CEL.gz
Reading in : celfiles//GSM1974608_S2_H70.CEL.gz
Reading in : celfiles//GSM1974609_S2_H71.CEL.gz
Reading in : celfiles//GSM1974610_S2_H72B.CEL.gz
Reading in : celfiles//GSM1974611_S2_H73.CEL.gz
Reading in : celfiles//GSM1974612_S1_H8.CEL.gz
Reading in : celfiles//GSM1974613_S1_H9.CEL.gz
Reading in : celfiles//GSM1974614_S2_H54B.CEL.gz
Reading in : celfiles//GSM1974615_S2_H55B.CEL.gz
Reading in : celfiles//GSM1974616_S2_H56B.CEL.gz
Reading in : celfiles//GSM1974617_S2_H57C.CEL.gz
Reading in : celfiles//GSM1974618_S2_H76.CEL.gz
Reading in : celfiles//GSM1974619_S2_H77.CEL.gz
Reading in : celfiles//GSM1974620_S2_H78.CEL.gz
Reading in : celfiles//GSM1974621_S2_H79.CEL.gz
Reading in : celfiles//GSM1974622_S2_H80.CEL.gz
Reading in : celfiles//GSM1974623_S2_H81.CEL.gz
Reading in : celfiles//GSM1974624_S2_H82.CEL.gz
Reading in : celfiles//GSM1974625_S2_H83.CEL.gz
Reading in : celfiles//GSM1974626_S2_H84.CEL.gz
Reading in : celfiles//GSM1974627_S2_H85B.CEL.gz
Reading in : celfiles//GSM1974628_S2_H88.CEL.gz
Reading in : celfiles//GSM1974629_S2_H89.CEL.gz
Reading in : celfiles//GSM1974630_S2_H90.CEL.gz
Reading in : celfiles//GSM1974631_S2_H91B.CEL.gz
Reading in : celfiles//GSM1974632_S3_H100C.CEL.gz
Reading in : celfiles//GSM1974633_S3_H102C.CEL.gz
Reading in : celfiles//GSM1974634_S3_H103C.CEL.gz
Reading in : celfiles//GSM1974635_S3_H104C.CEL.gz
Reading in : celfiles//GSM1974636_S3_H105C.CEL.gz
Reading in : celfiles//GSM1974637_S3_H106C.CEL.gz
Reading in : celfiles//GSM1974638_S3_H107C.CEL.gz
Reading in : celfiles//GSM1974639_S3_H108C.CEL.gz
Reading in : celfiles//GSM1974640_S3_H109C.CEL.gz
Reading in : celfiles//GSM1974641_S3_H110C.CEL.gz
Reading in : celfiles//GSM1974642_S3_H111C.CEL.gz
Reading in : celfiles//GSM1974643_S3_H113C.CEL.gz
Reading in : celfiles//GSM1974644_S3_H114C.CEL.gz
Reading in : celfiles//GSM1974645_S3_H115.CEL.gz
Reading in : celfiles//GSM1974646_S3_H116C.CEL.gz
Reading in : celfiles//GSM1974647_S3_H117C.CEL.gz
Reading in : celfiles//GSM1974648_S3_H118C.CEL.gz
Reading in : celfiles//GSM1974649_S3_H119C.CEL.gz
Reading in : celfiles//GSM1974650_S3_H120C.CEL.gz
Reading in : celfiles//GSM1974651_S3_H121C.CEL.gz
Reading in : celfiles//GSM1974652_S3_H122C.CEL.gz
Reading in : celfiles//GSM1974653_S3_H123C.CEL.gz
Reading in : celfiles//GSM1974654_S3_H124C.CEL.gz
Reading in : celfiles//GSM1974655_S3_H125C.CEL.gz
Reading in : celfiles//GSM1974656_S3_H126C.CEL.gz
Reading in : celfiles//GSM1974657_S3_H127C.CEL.gz
Reading in : celfiles//GSM1974658_S3_H128C.CEL.gz
Reading in : celfiles//GSM1974659_S3_H129C.CEL.gz
Reading in : celfiles//GSM1974660_S3_H130.CEL.gz
Reading in : celfiles//GSM1974661_S3_H131.CEL.gz
Reading in : celfiles//GSM1974662_S3_H132.CEL.gz
Reading in : celfiles//GSM1974663_S3_H133D.CEL.gz
Reading in : celfiles//GSM1974664_S3_H134.CEL.gz
Reading in : celfiles//GSM1974665_S3_H135B.CEL.gz
Reading in : celfiles//GSM1974666_S3_H136B.CEL.gz
Reading in : celfiles//GSM1974667_S3_H137B.CEL.gz
Reading in : celfiles//GSM1974668_S3_H138.CEL.gz
Reading in : celfiles//GSM1974669_S3_H139.CEL.gz
Reading in : celfiles//GSM1974670_S3_H140.CEL.gz
Reading in : celfiles//GSM1974671_S3_H141.CEL.gz
Reading in : celfiles//GSM1974672_S3_H142.CEL.gz
Reading in : celfiles//GSM1974673_S3_H143.CEL.gz
Reading in : celfiles//GSM1974674_S3_H144.CEL.gz
Reading in : celfiles//GSM1974675_S3_H145.CEL.gz
Reading in : celfiles//GSM1974676_S3_H146.CEL.gz
Reading in : celfiles//GSM1974677_S3_H147.CEL.gz
Reading in : celfiles//GSM1974678_S3_H148.CEL.gz
Reading in : celfiles//GSM1974679_S3_H149.CEL.gz
Reading in : celfiles//GSM1974680_S3_H150.CEL.gz
Reading in : celfiles//GSM1974681_S3_H151.CEL.gz
Reading in : celfiles//GSM1974682_S3_H152.CEL.gz
Reading in : celfiles//GSM1974683_S3_H153.CEL.gz
Reading in : celfiles//GSM1974684_S3_H154.CEL.gz
Reading in : celfiles//GSM1974685_S3_H155.CEL.gz
Reading in : celfiles//GSM1974686_S3_H156.CEL.gz
Reading in : celfiles//GSM1974687_S3_H157.CEL.gz
Reading in : celfiles//GSM1974688_S3_H158.CEL.gz
Reading in : celfiles//GSM1974689_S3_H159.CEL.gz
Reading in : celfiles//GSM1974690_S3_H160.CEL.gz
Reading in : celfiles//GSM1974691_S3_H161.CEL.gz
Reading in : celfiles//GSM1974692_S3_H162.CEL.gz
Reading in : celfiles//GSM1974693_S3_H163.CEL.gz
Reading in : celfiles//GSM1974694_S3_H164C.CEL.gz
Reading in : celfiles//GSM1974695_S3_H165.CEL.gz
Reading in : celfiles//GSM1974696_S3_H166.CEL.gz
Reading in : celfiles//GSM1974697_S3_H167.CEL.gz
Reading in : celfiles//GSM1974698_S3_H168.CEL.gz
Reading in : celfiles//GSM1974699_S3_H170.CEL.gz
Reading in : celfiles//GSM1974700_S3_H171.CEL.gz
Reading in : celfiles//GSM1974701_S3_H172B.CEL.gz
Reading in : celfiles//GSM1974702_S3_H173.CEL.gz
Reading in : celfiles//GSM1974703_S3_H174.CEL.gz
Reading in : celfiles//GSM1974704_S3_H175B.CEL.gz
Reading in : celfiles//GSM1974705_S3_H176.CEL.gz
Reading in : celfiles//GSM1974706_S3_H177.CEL.gz
Reading in : celfiles//GSM1974707_S3_H178.CEL.gz
Reading in : celfiles//GSM1974708_S3_H179.CEL.gz
Reading in : celfiles//GSM1974709_S3_H180.CEL.gz
Reading in : celfiles//GSM1974710_S3_H181.CEL.gz
Reading in : celfiles//GSM1974711_S3_H182.CEL.gz
Reading in : celfiles//GSM1974712_S3_H183.CEL.gz
Reading in : celfiles//GSM1974713_S3_H184.CEL.gz
Reading in : celfiles//GSM1974714_S3_H185.CEL.gz
Reading in : celfiles//GSM1974715_S3_H186.CEL.gz
Reading in : celfiles//GSM1974716_S3_H187B.CEL.gz
Reading in : celfiles//GSM1974717_S3_H188.CEL.gz
Reading in : celfiles//GSM1974718_S3_H189.CEL.gz
Reading in : celfiles//GSM1974719_S3_H190.CEL.gz
Reading in : celfiles//GSM1974720_S3_H191B.CEL.gz
Reading in : celfiles//GSM1974721_S3_H193.CEL.gz
Reading in : celfiles//GSM1974722_S3_H194.CEL.gz
Reading in : celfiles//GSM1974723_S3_H196.CEL.gz
Reading in : celfiles//GSM1974724_S3_H197.CEL.gz
Reading in : celfiles//GSM1974725_S3_H198.CEL.gz
Reading in : celfiles//GSM1974726_S3_H199.CEL.gz
Reading in : celfiles//GSM1974727_S3_H200.CEL.gz
Reading in : celfiles//GSM1974728_S3_H201.CEL.gz
Reading in : celfiles//GSM1974729_S3_H202.CEL.gz
Reading in : celfiles//GSM1974730_S3_H203.CEL.gz
Reading in : celfiles//GSM1974731_S3_H204.CEL.gz
Reading in : celfiles//GSM1974732_S3_H205B.CEL.gz
Reading in : celfiles//GSM1974733_S3_H206B.CEL.gz
Reading in : celfiles//GSM1974734_S3_H207B.CEL.gz
Reading in : celfiles//GSM1974735_S3_H208B.CEL.gz
Reading in : celfiles//GSM1974736_S3_H209B.CEL.gz
Reading in : celfiles//GSM1974737_S3_H210B.CEL.gz
Reading in : celfiles//GSM1974738_S3_H211B.CEL.gz
Reading in : celfiles//GSM1974739_S3_H212.CEL.gz
Reading in : celfiles//GSM1974740_S3_H213.CEL.gz
Reading in : celfiles//GSM1974741_S3_H214.CEL.gz
Reading in : celfiles//GSM1974742_S3_H215.CEL.gz
Reading in : celfiles//GSM1974743_S3_H216.CEL.gz
Reading in : celfiles//GSM1974744_S3_H217.CEL.gz
Reading in : celfiles//GSM1974745_S3_H218.CEL.gz
Reading in : celfiles//GSM1974746_S3_H219.CEL.gz
Reading in : celfiles//GSM1974747_S3_H220.CEL.gz
Reading in : celfiles//GSM1974748_S3_H221.CEL.gz
Reading in : celfiles//GSM1974749_S3_H222.CEL.gz
Reading in : celfiles//GSM1974750_S3_H224.CEL.gz
Reading in : celfiles//GSM1974751_S3_H225B.CEL.gz
Reading in : celfiles//GSM1974752_S3_H226B.CEL.gz
Reading in : celfiles//GSM1974753_S3_H227B.CEL.gz
Reading in : celfiles//GSM1974754_S3_H228.CEL.gz
Reading in : celfiles//GSM1974755_S3_H229.CEL.gz
Reading in : celfiles//GSM1974756_S3_H230.CEL.gz
Reading in : celfiles//GSM1974757_S3_H93C.CEL.gz
Reading in : celfiles//GSM1974758_S3_H94C.CEL.gz
Reading in : celfiles//GSM1974759_S3_H95C.CEL.gz
Reading in : celfiles//GSM1974760_S3_H96C.CEL.gz
Reading in : celfiles//GSM1974761_S3_H97CC.CEL.gz
Reading in : celfiles//GSM1974762_S3_H98C.CEL.gz
Reading in : celfiles//GSM1974763_S3_H99C.CEL.gz
Reading in : celfiles//GSM1978883_S1_H11.CEL.gz
Reading in : celfiles//GSM1978884_S1_H12.CEL.gz
Reading in : celfiles//GSM1978885_S1_H13.CEL.gz
Reading in : celfiles//GSM1978886_S1_H15.CEL.gz
Reading in : celfiles//GSM1978887_S1_H16.CEL.gz
Reading in : celfiles//GSM1978888_S1_H17.CEL.gz
Reading in : celfiles//GSM1978889_S1_H18.CEL.gz
Reading in : celfiles//GSM1978890_S1_H1B.CEL.gz
Reading in : celfiles//GSM1978891_S1_H21.CEL.gz
Reading in : celfiles//GSM1978892_S1_H23.CEL.gz
Reading in : celfiles//GSM1978893_S1_H25.CEL.gz
Reading in : celfiles//GSM1978894_S1_H30.CEL.gz
Reading in : celfiles//GSM1978895_S1_H32.CEL.gz
Reading in : celfiles//GSM1978896_S1_H37.CEL.gz
Reading in : celfiles//GSM1978897_S1_H39.CEL.gz
Reading in : celfiles//GSM1978898_S1_H42.CEL.gz
Reading in : celfiles//GSM1978899_S1_H5.CEL.gz
Reading in : celfiles//GSM1978900_S3_H195.CEL.gz
Reading in : celfiles//GSM1978901_S3_H223.CEL.gz
Reading in : celfiles//GSM1978902_S4_H231.CEL.gz
Reading in : celfiles//GSM1978903_S4_H232.CEL.gz
Reading in : celfiles//GSM1978904_S4_H233.CEL.gz
Reading in : celfiles//GSM1978905_S4_H234.CEL.gz
Reading in : celfiles//GSM1978906_S4_H235.CEL.gz
Reading in : celfiles//GSM1978907_S4_H236.CEL.gz
Reading in : celfiles//GSM1978908_S4_H237.CEL.gz
Reading in : celfiles//GSM1978909_S4_H238.CEL.gz
Reading in : celfiles//GSM1978910_S4_H239.CEL.gz
Reading in : celfiles//GSM1978911_S4_H240.CEL.gz
Reading in : celfiles//GSM1978912_S4_H241.CEL.gz
Reading in : celfiles//GSM1978913_S4_H242.CEL.gz
Reading in : celfiles//GSM1978914_S4_H243.CEL.gz
Reading in : celfiles//GSM1978915_S4_H244.CEL.gz
Reading in : celfiles//GSM1978916_S4_H245.CEL.gz
Reading in : celfiles//GSM1978917_S4_H246.CEL.gz
Reading in : celfiles//GSM1978918_S4_H247.CEL.gz
Reading in : celfiles//GSM1978919_S4_H248.CEL.gz
Reading in : celfiles//GSM1978920_S4_H249.CEL.gz
Reading in : celfiles//GSM1978921_S4_H250.CEL.gz
Reading in : celfiles//GSM1978922_S4_H251.CEL.gz
Reading in : celfiles//GSM1978923_S4_H252.CEL.gz
Reading in : celfiles//GSM1978924_S4_H253.CEL.gz
Reading in : celfiles//GSM1978925_S4_H254.CEL.gz
Reading in : celfiles//GSM1978926_S4_H255.CEL.gz
Reading in : celfiles//GSM1978927_S4_H256.CEL.gz
Reading in : celfiles//GSM1978928_S4_H257B.CEL.gz
Reading in : celfiles//GSM1978929_S4_H258B.CEL.gz
Reading in : celfiles//GSM1978930_S4_H259.CEL.gz
Reading in : celfiles//GSM1978931_S4_H261.CEL.gz
Reading in : celfiles//GSM1978932_S4_H262.CEL.gz
Reading in : celfiles//GSM1978933_S4_H263.CEL.gz
Reading in : celfiles//GSM1978934_S4_H264.CEL.gz
Reading in : celfiles//GSM1978935_S4_H265.CEL.gz
Reading in : celfiles//GSM1978936_S4_H266.CEL.gz
Reading in : celfiles//GSM1978937_S4_H267.CEL.gz
Reading in : celfiles//GSM1978938_S4_H268.CEL.gz
Reading in : celfiles//GSM1978939_S4_H269.CEL.gz
Reading in : celfiles//GSM1978940_S4_H270.CEL.gz
Reading in : celfiles//GSM1978941_S4_H271.CEL.gz
Reading in : celfiles//GSM1978942_S4_H272.CEL.gz
Reading in : celfiles//GSM1978943_S4_H273.CEL.gz
Reading in : celfiles//GSM1978944_S4_H274.CEL.gz
Reading in : celfiles//GSM1978945_S4_H275B.CEL.gz
Reading in : celfiles//GSM1978946_S4_H276B.CEL.gz
Reading in : celfiles//GSM1978947_S4_H278.CEL.gz
Reading in : celfiles//GSM1978948_S4_H279B.CEL.gz
Reading in : celfiles//GSM1978949_S4_H280B.CEL.gz
Warning in read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset)) :
  'channel' automatically added to varMetadata in phenoData.
# saveRDS(object = rawData, "rawData.RDS")
rawData <- readRDS("rawData.RDS")

Looking at the dimensions of the raw expression matrix.

exprs(rawData) %>% dim()
[1] 1354896     265

Plotting some metadata attributes

Looking at the number of samples for triple negative status and for set.

mdata_subset %>% 
  count(triple_negative_status, set)
mdata_subset %>% 
  count(triple_negative_status) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = triple_negative_status)) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Proportion",
       title = str_wrap("Proportions of TNBC status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_tnbc_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, pr) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = pr,
                         y = proportion,
                         fill = triple_negative_status)) +
    geom_text(aes(x = pr, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Progesterone Receptor Status",
       y = "Proportion",
       title = str_wrap("Proportions of progesterone receptor status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_pr_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, er) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = er,
                         y = proportion,
                         fill = triple_negative_status)) +
      geom_text(aes(x = er, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Estrogen Receptor Status",
       y = "Proportion",
       title = str_wrap("Proportions of estrogen receptor status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_er_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, her2) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = her2,
                         y = proportion,
                         fill = triple_negative_status)) +
    geom_text(aes(x = her2, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "HER2 Amplification Status",
       y = "Proportion",
       title = str_wrap("Proportions of HER2 amplification status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_her2_proportion_barplot.png")
Saving 5.03 x 3.11 in image

list("ER" = mdata_subset$geo_accession[mdata_subset$er == "Positive"],
     "PR" = mdata_subset$geo_accession[mdata_subset$pr == "Positive"],
     "HER2" = mdata_subset$geo_accession[mdata_subset$her2 == "Positive"]) %>%
  fromList(.) %>% 
  upset(., c("ER", "PR", "HER2"), width_ratio = 0.2) +
  ggtitle(str_wrap("Upset plot for different combinations of ER, PR, and HER2 status in the non-TNBC samples in GSE76275", 60)) +
  theme(title = element_text(size = 7))

ggsave("plots/exploration_plots/GSE76275_nonTNBC_upset.png")
Saving 5.03 x 3.11 in image

Reordering the columns in the metadata.

mdata <- select(mdata, geo_accession, everything())
head(mdata)

Performing RMA

Using regular RMA on data (without separating by class)

res_1 <- rma(rawData)
Loading required package: RSQLite
Loading required package: DBI
Background correcting
Normalizing
Calculating Expression
# saveRDS(object = res_1, "res_1.RDS")
res_1 <- readRDS("res_1.RDS")
exprs(res_1) %>% 
  dim()
[1] 54675   265
exprs(res_1)[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.754163  11.361803   9.690693  10.157010
1053_at     8.625663   9.011796   7.854072   7.925281
117_at      7.333973   7.385199   7.466878   8.048563
121_at      9.077887   8.782080   8.885189   8.775218
1255_g_at   4.656331   4.635625   4.536114   4.626950
          GSM1974570
1007_s_at  10.597699
1053_at     8.456781
117_at      7.581895
121_at      8.752407
1255_g_at   5.454820

Performing class-specific RMA by reading in the expression sets separately

Getting lists of the TNBC samples and the non-TNBC samples.

tnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(tnbc_samples)
[1] "GSM1974566" "GSM1974567" "GSM1974568" "GSM1974569"
[5] "GSM1974570" "GSM1974571"
nontnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(nontnbc_samples)
[1] "GSM1978883" "GSM1978884" "GSM1978885" "GSM1978886"
[5] "GSM1978887" "GSM1978888"

Creating different metadata tables for TNBC and nonTNBC.

mdata_subset_tnbc <- mdata_subset[tnbc_samples, ]
dim(mdata_subset_tnbc)
[1] 198  12
mdata_subset_nontnbc <- mdata_subset[nontnbc_samples, ]
dim(mdata_subset_nontnbc)
[1] 67 12

Reading in the TNBC files.

# rawData_tnbc <- read.celfiles(filenames = celFiles[tnbc_samples], 
#                               phenoData = AnnotatedDataFrame(mdata_subset_tnbc))
# 
# rawData_tnbc
# saveRDS(rawData_tnbc, file = "rawData_tnbc.RDS")
rawData_tnbc <- readRDS(file = "rawData_tnbc.RDS")
rawData_tnbc
ExpressionFeatureSet (storageMode: lockedEnvironment)
assayData: 1354896 features, 198 samples 
  element names: exprs 
protocolData
  rowNames: GSM1974566 GSM1974567 ... GSM1974763 (198 total)
  varLabels: exprs dates
  varMetadata: labelDescription channel
phenoData
  rowNames: GSM1974566 GSM1974567 ... GSM1974763 (198 total)
  varLabels: geo_accession title ... age_years (11 total)
  varMetadata: labelDescription channel
featureData: none
experimentData: use 'experimentData(object)'
Annotation: pd.hg.u133.plus.2 
Loading required package: pd.hg.u133.plus.2
Loading required package: RSQLite
Loading required package: DBI

Reading in the nonTNBC files.

# rawData_nontnbc <- read.celfiles(filenames = celFiles[nontnbc_samples], 
#                               phenoData = AnnotatedDataFrame(mdata_subset_nontnbc))
# 
# rawData_nontnbc
# saveRDS(rawData_nontnbc, file = "rawData_nontnbc.RDS")
rawData_nontnbc <- readRDS(file = "rawData_nontnbc.RDS")
rawData_nontnbc
ExpressionFeatureSet (storageMode: lockedEnvironment)
assayData: 1354896 features, 67 samples 
  element names: exprs 
protocolData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: exprs dates
  varMetadata: labelDescription channel
phenoData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: geo_accession title ... age_years (11
    total)
  varMetadata: labelDescription channel
featureData: none
experimentData: use 'experimentData(object)'
Annotation: pd.hg.u133.plus.2 

Performing RMA on TNBC data.

# res_tnbc <- rma(rawData_tnbc)
# saveRDS(res_tnbc, file = "res_tnbc.RDS")
res_tnbc <- readRDS(file = "res_tnbc.RDS")

Performing RMA on nonTNBC data.

# res_nontnbc <- rma(rawData_nontnbc)
# saveRDS(res_nontnbc, file = "res_nontnbc.RDS")
res_nontnbc <- readRDS(file = "res_nontnbc.RDS")

Combining the expression matrices of TNBC and nonTNBC data after separate RMA.

res_joint <- cbind(exprs(res_tnbc), exprs(res_nontnbc))
res_joint[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.703231  11.345325   9.732361  10.175704
1053_at     8.605269   9.019553   7.794234   7.945029
117_at      7.360884   7.373951   7.481344   8.047586
121_at      9.100729   8.776369   8.860402   8.755606
1255_g_at   4.664492   4.628692   4.569530   4.627230
          GSM1974570
1007_s_at  10.576463
1053_at     8.497373
117_at      7.530427
121_at      8.766560
1255_g_at   5.467292

Saving certain CSV files for everyone else to refer to

Saving the joint expression matrix from class-specific QN.

res_joint %>% 
  as_tibble(rownames = "probe_id") %>% 
  write_csv("dataframe_files/post_classQN_expression.csv")
Error: Cannot open file for writing:
* 'dataframe_files/post_classQN_expression.csv'

Saving a subset of the metadata that I think is relevant.

mdata_subset %>% 
  write_csv("dataframe_files/metadata_subset.csv")

Saving the TNBC and nonTNBC metadata separately, just in case.

mdata_subset_tnbc %>% 
  write_csv("dataframe_files/metadata_subset_tnbc.csv")
mdata_subset_nontnbc %>% 
  write_csv("dataframe_files/metadata_subset_nontnbc.csv")

Getting sample-specific boxplots

Sample specific boxplots for regular RMA QN

res_1_df_long <- res_1 %>%
  exprs() %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity") %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession"))
# saveRDS(object = res_1_df_long, "res_1_df_long.RDS")
res_1_df_long <- readRDS("res_1_df_long.RDS")
p2 <- res_1_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p2 <- p2 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for global quantile normalization for GSE76275", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")
p2

NA
ggsave("plots/exploration_plots/GSE76275_post_regQN_boxplots.png", 
       p2, 
       units = "cm", 
       width = 30, 
       height = 10)
rm(res_1_df_long)

Sample specific boxplots for classQN

res_joint_df_long <- res_joint %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity")
  
# saveRDS(object = res_joint_df_long, "res_joint_df_long.RDS")
res_joint_df_long <- readRDS("res_joint_df_long.RDS")
p1 <- res_joint_df_long %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession")) %>% 
  mutate(sample_id = factor(sample_id)) %>% 
  ggplot() +
   geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p1 <- p1 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for class-specific quantile normalization for GSE76275", 60)) +
scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")

p1 

ggsave("plots/exploration_plots/GSE76275_post_classQN_boxplots.png", 
       p1, 
       units = "cm", width = 20, height = 10)
res_joint_df_long %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession")) %>% 
  mutate(sample_id = factor(sample_id)) %>% 
  ggplot() +
   geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, fill = set), outlier.size = 0.2) +
labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for class-specific quantile normalization for GSE76275", 60)) +
scale_fill_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "right", 
        legend.direction = "vertical")

ggsave("plots/exploration_plots/GSE76275_post_classQN_boxplots_bad.png", units = "cm", width = 20, height = 10)

Performing PCA

Custom functions

Function to create an annotated data frame by combining PC scores as well as metadata: useful for ggplot visualization.

get_pca_annot_df <- function(pca.obj, sample_id_col, mdata_df){
  ind_scores <- pca.obj$x
  ind_scores_reordered <- ind_scores[match(rownames(ind_scores), mdata_df[[sample_id_col]]), ] %>% 
    as_tibble(rownames = sample_id_col) %>% 
    mutate(filename = factor(!!sym(sample_id_col)))
  ind_scores_annot <- left_join(ind_scores_reordered, y = mdata_df, by = sample_id_col) %>% 
  select(all_of(colnames(mdata_subset)), contains("PC"))
  return(ind_scores_annot)
}

Performing PCA on regular RMA data

# pca.res_1 <- res_1 %>% 
#   exprs() %>% 
#   t() %>% 
#   prcomp(center = TRUE, scale = TRUE)
# saveRDS(pca.res_1, "pca_res1.RDS")
pca.res_1 <- readRDS("pca_res1.RDS")

Getting the annotated data frame for the PCA.

pca.res_1.annot_df <- get_pca_annot_df(pca.obj = pca.res_1, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_1.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_1) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for global RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_scree.png", bg = "white")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores. The PCA does not seem to separate the TNBC and nonTNBC samples that well when regular RMA is performed.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after global quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))


ggsave("plots/exploration_plots/PCA_wholeQN_TNBC_status.png", bg = "white")
Saving 5.03 x 3.11 in image

NA

The samples do not seem to separate well by set either.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after global quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_set.png", bg = "white")
Saving 5.03 x 3.11 in image

There does not seem to be too strong of a batch effect according to submission date.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = submission_date)) +
    ggtitle("Samples in first two PCs, \ncoloured by submission date for whole QN") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_set.png")
ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = tnbc_subtype)) +
    ggtitle("Samples in first two PCs, \ncoloured by tnbc_subtype for whole QN") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5), legend.position = "right", legend.direction = "vertical", legend.key.width = unit(x = 0.5, units = "cm")) 

Performing PCA on separately-performed RMA data

pca.res_joint <- res_joint %>% 
  t() %>% 
  prcomp(center = TRUE, scale = TRUE)
# saveRDS(pca.res_joint, "pca_res_joint.RDS")
pca.res_joint <- readRDS("pca_res_joint.RDS")

Getting the annotated data frame for the PCA.

pca.res_joint.annot_df <- get_pca_annot_df(pca.obj = pca.res_joint, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_joint.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_joint) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for class-specific RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_scree.png")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores. The PCA does separate the TNBC and nonTNBC samples well when class-specific RMA is performed.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_status.png")
Saving 5.03 x 3.11 in image

The validation nonTNBC samples are separated from the discovery TNBC and validation TNBC samples.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
        title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_set.png")
Saving 5.03 x 3.11 in image

Submission date is perfectly confounded with TNBC status. May or may not be batch effects.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = submission_date)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by submission date for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Submission Date") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_date.png")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = tnbc_subtype)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by subtype of TNBC for GSE76275 after class-specific quantile normalization", 60)) +
   scale_color_nejm(name = "TNBC Subtype") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.text = element_text(size = 5), legend.key.height = unit(x = 0.3, units = "cm"), legend.key.width = unit(x = 0.3, units = "cm")) +
  guides(color = guide_legend(override.aes = list(size = 1)))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_subtype.png")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(pr), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by progesterone receptor status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Progesterone Receptor Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) +
    guides(color = guide_legend(override.aes = list(size = 1)))


ggsave("plots/exploration_plots/PCA_classQN_pr_GSE76275.png", bg= "white")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(her2), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by HER2 amplification status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_nejm(name = "HER2 Amplification Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) + 
      guides(color = guide_legend(override.aes = list(size = 1)))

ggsave("plots/exploration_plots/PCA_classQN_her2_GSE76275.png", bg = "white")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(er), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by estrogen receptor status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Estrogen Receptor Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) + 
      guides(color = guide_legend(override.aes = list(size = 1)))


ggsave("plots/exploration_plots/PCA_classQN_er_GSE76275.png")
Saving 5.03 x 3.11 in image

Performing hierarchical clustering

Getting distances

perform_min_max <- function(x){
  mm_transformation <- preProcess(x, method = "range")
  rescaled <- predict(mm_transformation, x)
  return(rescaled)
}

Getting distances after performing min max normalization.

# res_1_dists <- exprs(res_1) %>% 
#   t() %>% 
#   perform_min_max() %>% 
#   dist(method = "euclidean")
  
# saveRDS(res_1_dists, "res_1_dists.RDS")
res_1_dists <- readRDS("res_1_dists.RDS")
# res_joint_dists <- res_joint %>% 
#     t() %>% 
#   perform_min_max() %>% 
#   dist(method = "euclidean")
  
# saveRDS(res_joint_dists, "res_joint_dists.RDS")
res_joint_dists <- readRDS("res_joint_dists.RDS")

Using dendrograms

res_1_dend <- res_1_dists %>% 
  hclust() %>% 
  as.dendrogram()
res_joint_dend <- res_joint_dists %>%
  hclust() %>% 
  as.dendrogram()
library(dendextend)
# res_1_dend %>% 
#   labels()
# res_1_dend %>% 
#   order.dendrogram()
# (res_1 %>% 
#   exprs() %>% 
#   colnames())[28]
  
res_1_dend_laborder <- res_1_dend %>% 
  labels()
mycolors <- ifelse(mdata_subset[res_1_dend_laborder, ]$triple_negative_status == "TN", "forestgreen", "maroon")
par(mar = c(10,2,1,1))
res_1_dend %>% 
  set("labels_cex", 0.1) %>% 
  plot()

colored_bars(colors = mycolors, dend = res_1_dend, rowLabels = "TN Status", add = TRUE)
res_joint_dend_laborder <- res_joint_dend %>% 
  labels()
mycolors <- ifelse(mdata_subset[res_joint_dend_laborder, ]$triple_negative_status == "TN", "forestgreen", "maroon")
par(mar = c(10,2,1,1))
res_joint_dend %>% 
  set("labels_cex", 0.1) %>% 
  plot()

colored_bars(colors = mycolors, dend = res_joint_dend, rowLabels = "TN Status", add = TRUE)

Using heatmaps

Function to process distance object into a distance matrix for heatmap visualization.

get_distmat <- function(x){
  distmat <- as.matrix(x)
  colnames(distmat) <- NULL
  diag(distmat) <- NA
  return(distmat)
}
row_annot <- mdata_subset %>% 
  mutate(er = factor(er), pr = factor(pr), her2 = factor(her2)) %>% 
  select(submission_date, triple_negative_status, pr, er, her2) %>% 
  rename(`TN status` = triple_negative_status)

head(row_annot)
set.seed(5)
row_colours <- list( "TN status" = c("darkgoldenrod4", "darkmagenta"), 
                     "submission_date" = pal_nejm()(2),
                     "pr" = pal_lancet()(9)[1:2], 
                     "er" = pal_lancet()(9)[8:9],
                     "her2" = pal_lancet()(9)[5:7])

names(row_colours[["TN status"]]) <- as.character(unique(row_annot[["TN status"]]))
names(row_colours$pr) <- as.character(unique(row_annot$pr))
names(row_colours$er) <- as.character(unique(row_annot$er))
names(row_colours$her2) <- as.character(unique(row_annot$her2))
names(row_colours$submission_date) <- as.character(unique(row_annot$submission_date))
str(row_colours)
List of 5
 $ TN status      : Named chr [1:2] "darkgoldenrod4" "darkmagenta"
  ..- attr(*, "names")= chr [1:2] "TN" "not TN"
 $ submission_date: Named chr [1:2] "#BC3C29FF" "#0072B5FF"
  ..- attr(*, "names")= chr [1:2] "Dec 17 2015" "Dec 22 2015"
 $ pr             : Named chr [1:2] "#00468BFF" "#ED0000FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ er             : Named chr [1:2] "#ADB6B6FF" "#1B1919FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ her2           : Named chr [1:3] "#925E9FFF" "#FDAF91FF" "#AD002AFF"
  ..- attr(*, "names")= chr [1:3] "Negative" "Positive" "Not Available"
my_colours <-  viridis(265^2, begin = 1, end = 0)
res_joint_dists %>% 
  get_distmat() %>% 
pheatmap(.,
         color = my_colours,
         annotation_row = row_annot,
         annotation_colors = row_colours,
         show_colnames = F,
         show_rownames = F,
         cutree_rows = 2,
         cutree_cols = 2,
         main = str_wrap("Heatmap of sample distances for class-specific QN expression matrix for GSE76275", 60),
         legend_labels = c("small distance", "large distance"),
         legend_breaks = c(min(., na.rm = TRUE), 
                         max(., na.rm = TRUE)), 
          filename = "plots/exploration_plots/classQN_clustering_heatmap_GSE76275.png")

Performing SVA

Performing SVA on regular RMA data

full_mod <- mdata_subset %>% 
  select(geo_accession, triple_negative_status) %>% 
  arrange(triple_negative_status) %>% 
  model.matrix(~triple_negative_status, data = .)

head(full_mod)
           (Intercept) triple_negative_statusTN
GSM1978883           1                        0
GSM1978884           1                        0
GSM1978885           1                        0
GSM1978886           1                        0
GSM1978887           1                        0
GSM1978888           1                        0
red_mod <- model.matrix(~1, data = mdata_subset)

head(red_mod)
           (Intercept)
GSM1974566           1
GSM1974567           1
GSM1974568           1
GSM1974569           1
GSM1974570           1
GSM1974571           1

Get number of significant surrogate variables.

n.sv.wholeQN <- num.sv(exprs(res_1), full_mod, method="leek")
n.sv.wholeQN
[1] 0
svobj.wholeQN <- sva(exprs(res_1), mod = full_mod, mod0 = red_mod, n.sv = 1)
sv_df.wholeQN <- tibble("geo_accession" = colnames(exprs(res_1)), "sv" = svobj.wholeQN$sv)

head(sv_df.wholeQN)
left_join(sv_df.wholeQN, mdata, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  # geom_col(mapping = aes(y = fct_reorder(geo_accession, sv, .fun = function(x){x}), x = sv, fill = set)) +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = set)) +
  theme_light() +
  labs(y = "Surrogate Variable Value", title = "Distribution of latent variable estimated by SVA for different grouping factors")
  

# ggsave("plots/exploration_plots/sva_grouping_normalRMA.png")

Performing SVA on class-specific quantile normalized data

Create full model matrix.

full_mod <- mdata_subset %>% 
  select(geo_accession, triple_negative_status) %>% 
  arrange(triple_negative_status) %>% 
  model.matrix(~triple_negative_status, data = .)

head(full_mod)
           (Intercept) triple_negative_statusTN
GSM1978883           1                        0
GSM1978884           1                        0
GSM1978885           1                        0
GSM1978886           1                        0
GSM1978887           1                        0
GSM1978888           1                        0

Create reduced model matrix.

red_mod <- model.matrix(~1, data = mdata_subset)

head(red_mod)
           (Intercept)
GSM1974566           1
GSM1974567           1
GSM1974568           1
GSM1974569           1
GSM1974570           1
GSM1974571           1

Get number of significant surrogate variables.

n.sv.classQN <- num.sv(res_joint, full_mod, method="leek")
n.sv.classQN
[1] 1

Perform SVA on classQN-normalized expression matrix.

svobj.classQN <- sva(res_joint, mod = full_mod, mod0 = red_mod, n.sv = n.sv.classQN)
Number of significant surrogate variables is:  1 
Iteration (out of 5 ):1  2  3  4  5  
sv_df.classQN <- tibble("geo_accession" = colnames(res_joint), "sv" = svobj.classQN$sv)

head(sv_df.classQN)
saveRDS(sv_df.classQN, "sv_df_classQN.RDS")
# sv_df.classQN <- readRDS("sv_df_classQN.RDS")
sv_df.classQN %>% 
ggplot() +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = er)) +
  # geom_point(mapping = aes(x = submission_date, y = sv, color = er)) +
  theme_light() 

  # labs(y = "Surrogate Variable Value", title = "Distribution of latent variable estimated by SVA for different grouping factors")
  

# ggsave("plots/exploration_plots/sva_grouping_classQN.png")

Trying to see if the SVA estimates a batch when QN is not applied

In this attempt, I perform no quantile normalization while performing RMA. If QN has not been performed and a surrogate variable shows up that corresponds to batch, batch effects are probably present.

rawData.summary <- rma(rawData, background = TRUE, normalize = FALSE)
rawData.summary_df_long <- rawData.summary %>% 
  exprs() %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity") %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession"))
p3 <- rawData.summary_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, 
                             color = set)) +
  labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots in the absence of QN", 60)) +
  scale_color_npg() +
  theme(axis.text.x = element_blank())

p3
ggsave("plots/exploration_plots/GSE76275_noQN_boxplots.png", 
       p3, 
       units = "cm", width = 30, height = 10)

Getting the number of surrogate variables in the absence of quantile normalization.

n.sv.nonorm <- num.sv(exprs(rawData.summary), full_mod, method="leek")

There is one surrogate variable present in the absence of QN.

n.sv.nonorm
svobj.nonorm <- sva(exprs(rawData.summary), mod = full_mod, mod0 = red_mod, n.sv = n.sv.nonorm)
sv_df.nonorm <- tibble("geo_accession" = colnames(exprs(rawData.summary)), "sv" = svobj.nonorm$sv)

head(sv_df.nonorm)
left_join(sv_df.nonorm, mdata, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  # geom_col(mapping = aes(y = fct_reorder(geo_accession, sv, .fun = function(x){x}), x = sv, fill = set)) +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = set)) +
  theme_light() +
  labs(y = "Surrogate Variable Value", 
       title = str_wrap("Distribution of latent variable estimated by SVA for different grouping factors", 60))

ggsave("plots/exploration_plots/sva_grouping_noQN.png")
LS0tCnRpdGxlOiAiRXhwbG9yZSBHU0U3NjI3NSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgTG9hZGluZyBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KEdFT3F1ZXJ5KQpsaWJyYXJ5KG9saWdvKQpsaWJyYXJ5KHN2YSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShkZW5kZXh0ZW5kKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KFVwU2V0UikKbGlicmFyeShDb21wbGV4VXBzZXQpCmBgYAoKCiMgQ3VzdG9tIGZ1bmN0aW9ucwoKYGBge3J9CiMgZ2l2ZW4gYSBtYXRyaXgsIHBlcmZvcm0gbWluLW1heCBzY2FsaW5nIG9uIGl0cyBjb2x1bW5zCm1pbl9tYXhfbWF0IDwtIGZ1bmN0aW9uKG1hdCl7CiAgbWF0X3Jlc2NhbGVkIDwtIGFwcGx5KG1hdCwgMiwgZnVuY3Rpb24odil7CiAgICB2X3JhbmdlIDwtIHJhbmdlKHYpCiAgICBuYW1lcyh2X3JhbmdlKSA8LSBjKCJtaW5pbXVtIiwgIm1heGltdW0iKQogICAgcmFuZ2VfZGlmZmVyZW5jZSA8LSB2X3JhbmdlWyJtYXhpbXVtIl0gLSB2X3JhbmdlWyJtaW5pbXVtIl0KICAgIHJlc2NhbGVkIDwtICh2IC0gdl9yYW5nZVsibWluaW11bSJdKS9yYW5nZV9kaWZmZXJlbmNlCiAgICByZXR1cm4ocmVzY2FsZWQpCiAgfSkKICByZXR1cm4obWF0X3Jlc2NhbGVkKQp9CmBgYAoKIyBHZXR0aW5nIGRhdGEgZnJvbSBHRU9xdWVyeQoKYGBge3J9CiMgZ2VvZGF0YSA8LSBHRU9xdWVyeTo6Z2V0R0VPKEdFTyA9ICJHU0U3NjI3NSIsIGRlc3RkaXIgPSAiLi90ZW1wZmlsZXMiKQojIGdlb2RhdGEgPC0gR0VPcXVlcnk6OmdldEdFTyhmaWxlbmFtZSA9ICIuL3RlbXBmaWxlcy9HU0U3NjI3NV9zZXJpZXNfbWF0cml4LnR4dC5neiIpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMoZ2VvZGF0YSwgImdlb2RhdGEuUkRTIikKZ2VvZGF0YSA8LSByZWFkUkRTKCJnZW9kYXRhLlJEUyIpCmBgYAoKCmBgYHtyfQojIG1kYXRhIDwtIGdlb2RhdGEgJT4lIAojICAgcGx1Y2soMSkgJT4lIAojICAgcGhlbm9EYXRhKCkgJT4lCiMgICBwRGF0YSgpICU+JSBhc190aWJibGUoKQpgYGAKCgpgYGB7cn0KIyBmZWF0dXJlX2RhdGEgPC0gZ2VvZGF0YSAlPiUgCiMgICBwbHVjaygxKSAlPiUgCiMgICBmZWF0dXJlRGF0YSgpCiAgCmBgYAoKCmBgYHtyfQojIHdyaXRlX2NzdihtZGF0YSwgInJhd19tZGF0YS5jc3YiKQptZGF0YSA8LSByZWFkX2NzdigicmF3X21kYXRhLmNzdiIpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMoZmVhdHVyZV9kYXRhLCAiZmVhdHVyZURhdGEuUkRTIikKIyBmZWF0dXJlX2RhdGEgPC0gcmVhZFJEUygiZmVhdHVyZURhdGEuUkRTIikKYGBgCgojIEluc3BlY3RpbmcgYW5kIGNsZWFuaW5nIHRoZSBtZXRhZGF0YQoKYGBge3J9Cm1kYXRhICU+JSAKICBnbGltcHNlKCkKYGBgCgoKYGBge3J9Cm1kYXRhIDwtIG1kYXRhICU+JSAKICBzZWxlY3QodGl0bGUsIGNvbnRhaW5zKCJkYXRlIiksIGdlb19hY2Nlc3Npb24sIGNvbnRhaW5zKCI6Y2gxIikpCgpjb2xuYW1lcyhtZGF0YSkKICAKYGBgCgoKYGBge3J9CmNuYW1lcyA8LSBjb2xuYW1lcyhtZGF0YSkKYGBgCgoKYGBge3J9CmNuYW1lc19wcm9jZXNzZWQgPC0gc3RyX3NwbGl0KGNuYW1lcywgcGF0dGVybiA9ICI6IikgJT4lIAogIG1hcF9jaHIofnsueFtbMV1dfSkgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiICIsICJfIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiLSIsICJfIikgJT4lIAogIHN0cl9yZW1vdmVfYWxsKCJcXCh8XFwpfCwiKQoKY25hbWVzX3Byb2Nlc3NlZApgYGAKCgpgYGB7cn0KY29sbmFtZXMobWRhdGEpIDwtIGNuYW1lc19wcm9jZXNzZWQKcm0oY25hbWVzLCBjbmFtZXNfcHJvY2Vzc2VkKQpgYGAKCgpgYGB7cn0KZ2xpbXBzZShtZGF0YSkKYGBgCgpgYGB7cn0KbWRhdGEgPC0gbWRhdGEgJT4lIAogIG11dGF0ZShoZXIyID0gaWZfZWxzZSghaXMubmEoaGVyMiksIGhlcjIsICJOb3QgQXZhaWxhYmxlIikpICU+JSAKICBtdXRhdGUoZXIgPSBmYWN0b3IoZXIsIGxldmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpLCAKICAgICAgICAgcHIgPSBmYWN0b3IocHIsIGxldmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpLCAKICAgICAgICAgaGVyMiA9IGZhY3RvcihoZXIyLCBsZXZlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIsICJOb3QgQXZhaWxhYmxlIikpKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24sIGV2ZXJ5dGhpbmcoKSkKaGVhZChtZGF0YSkgCmBgYAoKIyBSZWFkaW5nIGluIHJhdyBwcm9iZSBpbnRlbnNpdHkgZGF0YQoKQ2VsZmlsZXMgZG93bmxvYWRlZCBmcm9tIEdFTyBhbmQga2VwdCB0aGUgZm9sZGVyIGNlbGZpbGVzLwoKYGBge3J9CmNlbEZpbGVzIDwtIGxpc3QuY2VsZmlsZXMoJ2NlbGZpbGVzLycsIGZ1bGwubmFtZXMgPSBUUlVFLCBsaXN0R3ppcHBlZCA9IFRSVUUpCmNlbEZpbGVzICU+JSBoZWFkKCkKYGBgCgoKCmBgYHtyfQpuYW1lcyhjZWxGaWxlcykgPC0gY2VsRmlsZXMgJT4lIAogIGJhc2VuYW1lKCkgJT4lIAogIHN0cl9zcGxpdCgiXFwuIikgJT4lIAogIG1hcF9jaHIofnsueFsxXX0pICU+JSAKICBzdHJfc3BsaXQoIl8iKSAlPiUgCiAgbWFwX2Nocih+ey54WzFdfSkgCgpoZWFkKGNlbEZpbGVzKQpgYGAKClJlYXJyYW5naW5nIHJvd3Mgb2YgbWV0YWRhdGEgdG8gbWF0Y2ggb3JkZXIgb2Ygc2FtcGxlcyBpbiBgY2VsRmlsZXNgLgoKYGBge3J9Cm1kYXRhIDwtIG1kYXRhW21hdGNoKG1kYXRhJGdlb19hY2Nlc3Npb24sIG5hbWVzKGNlbEZpbGVzKSksIF0KYGBgCgoKR2V0dGluZyBvbmx5IHRoZSByZWxldmFudCB2YXJpYWJsZXMgZnJvbSB0aGUgbWV0YWRhdGEuCgpgYGB7cn0KbWRhdGFfc3Vic2V0IDwtIG1kYXRhICU+JQogIHNlbGVjdChnZW9fYWNjZXNzaW9uLCAKICAgICAgICAgdGl0bGUsIAogICAgICAgICB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCAKICAgICAgICAgdG5iY19zdWJ0eXBlLAogICAgICAgICBzdWJtaXNzaW9uX2RhdGUsCiAgICAgICAgIGVyLAogICAgICAgICBoZXIyLAogICAgICAgICBwciwKICAgICAgICAgcmFjZSwKICAgICAgICAgc2V0LAogICAgICAgICBnZW5kZXIsIAogICAgICAgICBhZ2VfeWVhcnMpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIC5mbnMgPSBmYWN0b3IpKSAlPiUgCiAgbXV0YXRlKHRuYmNfc3VidHlwZSA9IGlmX2Vsc2UoaXMubmEoYXMuY2hhcmFjdGVyKHRuYmNfc3VidHlwZSkpLCAiTm90IEFwcGxpY2FibGUiLCBhcy5jaGFyYWN0ZXIodG5iY19zdWJ0eXBlKSkpICU+JSAKICBtdXRhdGUodG5iY19zdWJ0eXBlID0gZmFjdG9yKHRuYmNfc3VidHlwZSkpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKCgpyb3duYW1lcyhtZGF0YV9zdWJzZXQpIDwtIGFzLmNoYXJhY3RlcihtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvbikKCmhlYWQobWRhdGFfc3Vic2V0KQoKYGBgCgpgYGB7cn0KIyByYXdEYXRhIDwtIHJlYWQuY2VsZmlsZXMoY2VsRmlsZXMsIHBoZW5vRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZShtZGF0YV9zdWJzZXQpKQpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKG9iamVjdCA9IHJhd0RhdGEsICJyYXdEYXRhLlJEUyIpCnJhd0RhdGEgPC0gcmVhZFJEUygicmF3RGF0YS5SRFMiKQpgYGAKCgpMb29raW5nIGF0IHRoZSBkaW1lbnNpb25zIG9mIHRoZSByYXcgZXhwcmVzc2lvbiBtYXRyaXguCgpgYGB7cn0KZXhwcnMocmF3RGF0YSkgJT4lIGRpbSgpCmBgYAoKIyBQbG90dGluZyBzb21lIG1ldGFkYXRhIGF0dHJpYnV0ZXMKCkxvb2tpbmcgYXQgdGhlIG51bWJlciBvZiBzYW1wbGVzIGZvciB0cmlwbGUgbmVnYXRpdmUgc3RhdHVzIGFuZCBmb3Igc2V0LgoKYGBge3J9Cm1kYXRhX3N1YnNldCAlPiUgCiAgY291bnQodHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgc2V0KQpgYGAKCgoKYGBge3J9Cm1kYXRhX3N1YnNldCAlPiUgCiAgY291bnQodHJpcGxlX25lZ2F0aXZlX3N0YXR1cykgJT4lIAogIG11dGF0ZShwcm9wb3J0aW9uID0gcm91bmQobi9zdW0obiksIDMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeSA9ICJkdW1teV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICBnZW9tX3RleHQoYWVzKHkgPSAxLCAKICAgICAgICAgICAgICAgIHggPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBncm91cCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpLCAKICAgICAgICAgICAgc2l6ZSA9IDUsIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICBsYWJzKHggPSAiUHJvcG9ydGlvbiIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJQcm9wb3J0aW9ucyBvZiBUTkJDIHN0YXR1cyB2YWx1ZXMgZm9yIEdTRTc2Mjc1IiwgNjApKQoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3RuYmNfcHJvcG9ydGlvbl9iYXJwbG90LnBuZyIpCmBgYAoKYGBge3J9Cm1kYXRhX3N1YnNldCAlPiUgCiAgY291bnQodHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgcHIpICU+JSAKICBtdXRhdGUocHJvcG9ydGlvbiA9IHJvdW5kKG4vc3VtKG4pLCAzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBwciwKICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZW9tX3RleHQoYWVzKHggPSBwciwgCiAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSwgCiAgICAgICAgICAgIHNpemUgPSAzLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIHNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICBsYWJzKHggPSAiUHJvZ2VzdGVyb25lIFJlY2VwdG9yIFN0YXR1cyIsCiAgICAgICB5ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2YgcHJvZ2VzdGVyb25lIHJlY2VwdG9yIHN0YXR1cyB2YWx1ZXMgZm9yIEdTRTc2Mjc1IiwgNjApKQoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3ByX3Byb3BvcnRpb25fYmFycGxvdC5wbmciKQpgYGAKCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBlcikgJT4lIAogIG11dGF0ZShwcm9wb3J0aW9uID0gcm91bmQobi9zdW0obiksIDMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgICAgZ2VvbV90ZXh0KGFlcyh4ID0gZXIsIAogICAgICAgICAgICAgICAgeSA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGdyb3VwID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cyksIAogICAgICAgICAgICBzaXplID0gMywgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICBzY2FsZV9maWxsX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgbGFicyh4ID0gIkVzdHJvZ2VuIFJlY2VwdG9yIFN0YXR1cyIsCiAgICAgICB5ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2YgZXN0cm9nZW4gcmVjZXB0b3Igc3RhdHVzIHZhbHVlcyBmb3IgR1NFNzYyNzUiLCA2MCkpCgpnZ3NhdmUoZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfZXJfcHJvcG9ydGlvbl9iYXJwbG90LnBuZyIpCmBgYAoKYGBge3J9Cm1kYXRhX3N1YnNldCAlPiUgCiAgY291bnQodHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgaGVyMikgJT4lIAogIG11dGF0ZShwcm9wb3J0aW9uID0gcm91bmQobi9zdW0obiksIDMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IGhlcjIsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgZ2VvbV90ZXh0KGFlcyh4ID0gaGVyMiwgCiAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSwgCiAgICAgICAgICAgIHNpemUgPSAzLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIHNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICBsYWJzKHggPSAiSEVSMiBBbXBsaWZpY2F0aW9uIFN0YXR1cyIsCiAgICAgICB5ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2YgSEVSMiBhbXBsaWZpY2F0aW9uIHN0YXR1cyB2YWx1ZXMgZm9yIEdTRTc2Mjc1IiwgNjApKQoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X2hlcjJfcHJvcG9ydGlvbl9iYXJwbG90LnBuZyIpCmBgYAoKCgpgYGB7cn0KbGlzdCgiRVIiID0gbWRhdGFfc3Vic2V0JGdlb19hY2Nlc3Npb25bbWRhdGFfc3Vic2V0JGVyID09ICJQb3NpdGl2ZSJdLAogICAgICJQUiIgPSBtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvblttZGF0YV9zdWJzZXQkcHIgPT0gIlBvc2l0aXZlIl0sCiAgICAgIkhFUjIiID0gbWRhdGFfc3Vic2V0JGdlb19hY2Nlc3Npb25bbWRhdGFfc3Vic2V0JGhlcjIgPT0gIlBvc2l0aXZlIl0pICU+JQogIGZyb21MaXN0KC4pICU+JSAKICB1cHNldCguLCBjKCJFUiIsICJQUiIsICJIRVIyIiksIHdpZHRoX3JhdGlvID0gMC4yKSArCiAgZ2d0aXRsZShzdHJfd3JhcCgiVXBzZXQgcGxvdCBmb3IgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBFUiwgUFIsIGFuZCBIRVIyIHN0YXR1cyBpbiB0aGUgbm9uLVROQkMgc2FtcGxlcyBpbiBHU0U3NjI3NSIsIDYwKSkgKwogIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfbm9uVE5CQ191cHNldC5wbmciKQpgYGAKClJlb3JkZXJpbmcgdGhlIGNvbHVtbnMgaW4gdGhlIG1ldGFkYXRhLgoKYGBge3J9Cm1kYXRhIDwtIHNlbGVjdChtZGF0YSwgZ2VvX2FjY2Vzc2lvbiwgZXZlcnl0aGluZygpKQpoZWFkKG1kYXRhKQpgYGAKCgojIFBlcmZvcm1pbmcgUk1BCgojIyBVc2luZyByZWd1bGFyIFJNQSBvbiBkYXRhICh3aXRob3V0IHNlcGFyYXRpbmcgYnkgY2xhc3MpCgpgYGB7cn0KIyByZXNfMSA8LSBybWEocmF3RGF0YSkKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhvYmplY3QgPSByZXNfMSwgInJlc18xLlJEUyIpCnJlc18xIDwtIHJlYWRSRFMoInJlc18xLlJEUyIpCmBgYAoKCmBgYHtyfQpleHBycyhyZXNfMSkgJT4lIAogIGRpbSgpCmBgYAoKCmBgYHtyfQpleHBycyhyZXNfMSlbMTo1LCAxOjVdCmBgYAoKCiMjIFBlcmZvcm1pbmcgY2xhc3Mtc3BlY2lmaWMgUk1BIGJ5IHJlYWRpbmcgaW4gdGhlIGV4cHJlc3Npb24gc2V0cyBzZXBhcmF0ZWx5CgoKR2V0dGluZyBsaXN0cyBvZiB0aGUgVE5CQyBzYW1wbGVzIGFuZCB0aGUgbm9uLVROQkMgc2FtcGxlcy4KCmBgYHtyfQp0bmJjX3NhbXBsZXMgPC0gbWRhdGFfc3Vic2V0ICU+JSAKICBmaWx0ZXIodHJpcGxlX25lZ2F0aXZlX3N0YXR1cyA9PSAiVE4iKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24pICU+JSAKICB1bmxpc3QodXNlLm5hbWVzID0gRikgJT4lIAogIGFzLmNoYXJhY3RlcigpCgpoZWFkKHRuYmNfc2FtcGxlcykKCm5vbnRuYmNfc2FtcGxlcyA8LSBtZGF0YV9zdWJzZXQgJT4lIAogIGZpbHRlcih0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJub3QgVE4iKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24pICU+JSAKICB1bmxpc3QodXNlLm5hbWVzID0gRikgJT4lIAogIGFzLmNoYXJhY3RlcigpCgpoZWFkKG5vbnRuYmNfc2FtcGxlcykKYGBgCgoKQ3JlYXRpbmcgZGlmZmVyZW50IG1ldGFkYXRhIHRhYmxlcyBmb3IgVE5CQyBhbmQgbm9uVE5CQy4KCmBgYHtyfQptZGF0YV9zdWJzZXRfdG5iYyA8LSBtZGF0YV9zdWJzZXRbdG5iY19zYW1wbGVzLCBdCmRpbShtZGF0YV9zdWJzZXRfdG5iYykKbWRhdGFfc3Vic2V0X25vbnRuYmMgPC0gbWRhdGFfc3Vic2V0W25vbnRuYmNfc2FtcGxlcywgXQpkaW0obWRhdGFfc3Vic2V0X25vbnRuYmMpCmBgYAoKClJlYWRpbmcgaW4gdGhlIFROQkMgZmlsZXMuCgpgYGB7cn0KIyByYXdEYXRhX3RuYmMgPC0gcmVhZC5jZWxmaWxlcyhmaWxlbmFtZXMgPSBjZWxGaWxlc1t0bmJjX3NhbXBsZXNdLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdGF0ZWREYXRhRnJhbWUobWRhdGFfc3Vic2V0X3RuYmMpKQojIAojIHJhd0RhdGFfdG5iYwpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKHJhd0RhdGFfdG5iYywgZmlsZSA9ICJyYXdEYXRhX3RuYmMuUkRTIikKcmF3RGF0YV90bmJjIDwtIHJlYWRSRFMoZmlsZSA9ICJyYXdEYXRhX3RuYmMuUkRTIikKYGBgCgpgYGB7cn0KcmF3RGF0YV90bmJjCmBgYAoKClJlYWRpbmcgaW4gdGhlIG5vblROQkMgZmlsZXMuCgpgYGB7cn0KIyByYXdEYXRhX25vbnRuYmMgPC0gcmVhZC5jZWxmaWxlcyhmaWxlbmFtZXMgPSBjZWxGaWxlc1tub250bmJjX3NhbXBsZXNdLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdGF0ZWREYXRhRnJhbWUobWRhdGFfc3Vic2V0X25vbnRuYmMpKQojIAojIHJhd0RhdGFfbm9udG5iYwpgYGAKCgoKYGBge3J9CiMgc2F2ZVJEUyhyYXdEYXRhX25vbnRuYmMsIGZpbGUgPSAicmF3RGF0YV9ub250bmJjLlJEUyIpCnJhd0RhdGFfbm9udG5iYyA8LSByZWFkUkRTKGZpbGUgPSAicmF3RGF0YV9ub250bmJjLlJEUyIpCmBgYAoKCmBgYHtyfQpyYXdEYXRhX25vbnRuYmMKYGBgCgoKUGVyZm9ybWluZyBSTUEgb24gVE5CQyBkYXRhLgoKYGBge3J9CiMgcmVzX3RuYmMgPC0gcm1hKHJhd0RhdGFfdG5iYykKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhyZXNfdG5iYywgZmlsZSA9ICJyZXNfdG5iYy5SRFMiKQpyZXNfdG5iYyA8LSByZWFkUkRTKGZpbGUgPSAicmVzX3RuYmMuUkRTIikKYGBgCgoKUGVyZm9ybWluZyBSTUEgb24gbm9uVE5CQyBkYXRhLgoKYGBge3J9CiMgcmVzX25vbnRuYmMgPC0gcm1hKHJhd0RhdGFfbm9udG5iYykKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhyZXNfbm9udG5iYywgZmlsZSA9ICJyZXNfbm9udG5iYy5SRFMiKQpyZXNfbm9udG5iYyA8LSByZWFkUkRTKGZpbGUgPSAicmVzX25vbnRuYmMuUkRTIikKYGBgCgoKQ29tYmluaW5nIHRoZSBleHByZXNzaW9uIG1hdHJpY2VzIG9mIFROQkMgYW5kIG5vblROQkMgZGF0YSBhZnRlciBzZXBhcmF0ZSBSTUEuCgpgYGB7cn0KcmVzX2pvaW50IDwtIGNiaW5kKGV4cHJzKHJlc190bmJjKSwgZXhwcnMocmVzX25vbnRuYmMpKQpgYGAKCgpgYGB7cn0KcmVzX2pvaW50WzE6NSwgMTo1XQpgYGAKCiMgU2F2aW5nIGNlcnRhaW4gQ1NWIGZpbGVzIGZvciBldmVyeW9uZSBlbHNlIHRvIHJlZmVyIHRvCgpTYXZpbmcgdGhlIGpvaW50IGV4cHJlc3Npb24gbWF0cml4IGZyb20gY2xhc3Mtc3BlY2lmaWMgUU4uCgpgYGB7cn0KcmVzX2pvaW50ICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAicHJvYmVfaWQiKSAlPiUgCiAgd3JpdGVfY3N2KCJkYXRhZnJhbWVfZmlsZXMvcG9zdF9jbGFzc1FOX2V4cHJlc3Npb24uY3N2IikKYGBgCgpTYXZpbmcgYSBzdWJzZXQgb2YgdGhlIG1ldGFkYXRhIHRoYXQgSSB0aGluayBpcyByZWxldmFudC4KCmBgYHtyfQptZGF0YV9zdWJzZXQgJT4lIAogIHdyaXRlX2NzdigiZGF0YWZyYW1lX2ZpbGVzL21ldGFkYXRhX3N1YnNldC5jc3YiKQpgYGAKCgpTYXZpbmcgdGhlIFROQkMgYW5kIG5vblROQkMgbWV0YWRhdGEgc2VwYXJhdGVseSwganVzdCBpbiBjYXNlLgoKYGBge3J9Cm1kYXRhX3N1YnNldF90bmJjICU+JSAKICB3cml0ZV9jc3YoImRhdGFmcmFtZV9maWxlcy9tZXRhZGF0YV9zdWJzZXRfdG5iYy5jc3YiKQpgYGAKCgpgYGB7cn0KbWRhdGFfc3Vic2V0X25vbnRuYmMgJT4lIAogIHdyaXRlX2NzdigiZGF0YWZyYW1lX2ZpbGVzL21ldGFkYXRhX3N1YnNldF9ub250bmJjLmNzdiIpCmBgYAoKCiMgR2V0dGluZyBzYW1wbGUtc3BlY2lmaWMgYm94cGxvdHMKCiMjIFNhbXBsZSBzcGVjaWZpYyBib3hwbG90cyBmb3IgcmVndWxhciBSTUEgUU4KCgpgYGB7cn0KcmVzXzFfZGZfbG9uZyA8LSByZXNfMSAlPiUKICBleHBycygpICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAicHJvYmVJRCIpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGFsbF9vZihjKHRuYmNfc2FtcGxlcywgbm9udG5iY19zYW1wbGVzKSksIG5hbWVzX3RvID0gInNhbXBsZV9pZCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogIGxlZnRfam9pbiguLCBtZGF0YV9zdWJzZXQsIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJnZW9fYWNjZXNzaW9uIikpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMob2JqZWN0ID0gcmVzXzFfZGZfbG9uZywgInJlc18xX2RmX2xvbmcuUkRTIikKcmVzXzFfZGZfbG9uZyA8LSByZWFkUkRTKCJyZXNfMV9kZl9sb25nLlJEUyIpCmBgYAoKCmBgYHtyfQpwMiA8LSByZXNfMV9kZl9sb25nICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoc2FtcGxlX2lkLCBhcy5udW1lcmljKHNldCkpLCB5ID0gaW50ZW5zaXR5LCBjb2xvciA9IHNldCksIG91dGxpZXIuc2l6ZSA9IDAuMikKYGBgCgoKYGBge3J9CnAyIDwtIHAyICsgbGFicyh4ID0gInNhbXBsZXMiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNhbXBsZS13aXNlIGxvZzIgaW50ZW5zaXR5IGJveHBsb3RzIGZvciBnbG9iYWwgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiBmb3IgR1NFNzYyNzUiLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLCAKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKQpwMgogIApgYGAKCgpgYGB7cn0KZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9wb3N0X3JlZ1FOX2JveHBsb3RzLnBuZyIsIAogICAgICAgcDIsIAogICAgICAgdW5pdHMgPSAiY20iLCAKICAgICAgIHdpZHRoID0gMzAsIAogICAgICAgaGVpZ2h0ID0gMTApCmBgYAoKCmBgYHtyfQpybShyZXNfMV9kZl9sb25nKQpgYGAKCgojIyBTYW1wbGUgc3BlY2lmaWMgYm94cGxvdHMgZm9yIGNsYXNzUU4KCgpgYGB7cn0KcmVzX2pvaW50X2RmX2xvbmcgPC0gcmVzX2pvaW50ICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAicHJvYmVJRCIpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGFsbF9vZihjKHRuYmNfc2FtcGxlcywgbm9udG5iY19zYW1wbGVzKSksIG5hbWVzX3RvID0gInNhbXBsZV9pZCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikKICAKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhvYmplY3QgPSByZXNfam9pbnRfZGZfbG9uZywgInJlc19qb2ludF9kZl9sb25nLlJEUyIpCnJlc19qb2ludF9kZl9sb25nIDwtIHJlYWRSRFMoInJlc19qb2ludF9kZl9sb25nLlJEUyIpCmBgYAoKCgpgYGB7cn0KcDEgPC0gcmVzX2pvaW50X2RmX2xvbmcgJT4lIAogIGxlZnRfam9pbiguLCBtZGF0YV9zdWJzZXQsIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJnZW9fYWNjZXNzaW9uIikpICU+JSAKICBtdXRhdGUoc2FtcGxlX2lkID0gZmFjdG9yKHNhbXBsZV9pZCkpICU+JSAKICBnZ3Bsb3QoKSArCiAgIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKHNhbXBsZV9pZCwgYXMubnVtZXJpYyhzZXQpKSwgeSA9IGludGVuc2l0eSwgY29sb3IgPSBzZXQpLCBvdXRsaWVyLnNpemUgPSAwLjIpCmBgYAoKCmBgYHtyfQpwMSA8LSBwMSArIGxhYnMoeCA9ICJzYW1wbGVzIiwgCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJTYW1wbGUtd2lzZSBsb2cyIGludGVuc2l0eSBib3hwbG90cyBmb3IgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiBmb3IgR1NFNzYyNzUiLCA2MCkpICsKc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikKCnAxIApgYGAKCgoKYGBge3J9Cmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfcG9zdF9jbGFzc1FOX2JveHBsb3RzLnBuZyIsIAogICAgICAgcDEsIAogICAgICAgdW5pdHMgPSAiY20iLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxMCkKYGBgCgoKYGBge3J9CnJlc19qb2ludF9kZl9sb25nICU+JSAKICBsZWZ0X2pvaW4oLiwgbWRhdGFfc3Vic2V0LCBieSA9IGMoInNhbXBsZV9pZCIgPSAiZ2VvX2FjY2Vzc2lvbiIpKSAlPiUgCiAgbXV0YXRlKHNhbXBsZV9pZCA9IGZhY3RvcihzYW1wbGVfaWQpKSAlPiUgCiAgZ2dwbG90KCkgKwogICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIGZpbGwgPSBzZXQpLCBvdXRsaWVyLnNpemUgPSAwLjIpICsKbGFicyh4ID0gInNhbXBsZXMiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNhbXBsZS13aXNlIGxvZzIgaW50ZW5zaXR5IGJveHBsb3RzIGZvciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIGZvciBHU0U3NjI3NSIsIDYwKSkgKwpzY2FsZV9maWxsX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3Bvc3RfY2xhc3NRTl9ib3hwbG90c19iYWQucG5nIiwgdW5pdHMgPSAiY20iLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxMCkKYGBgCgoKIyBQZXJmb3JtaW5nIFBDQQoKIyMgQ3VzdG9tIGZ1bmN0aW9ucwoKRnVuY3Rpb24gdG8gY3JlYXRlIGFuIGFubm90YXRlZCBkYXRhIGZyYW1lIGJ5IGNvbWJpbmluZyBQQyBzY29yZXMgYXMgd2VsbCBhcyBtZXRhZGF0YTogdXNlZnVsIGZvciBnZ3Bsb3QgdmlzdWFsaXphdGlvbi4KCmBgYHtyfQpnZXRfcGNhX2Fubm90X2RmIDwtIGZ1bmN0aW9uKHBjYS5vYmosIHNhbXBsZV9pZF9jb2wsIG1kYXRhX2RmKXsKICBpbmRfc2NvcmVzIDwtIHBjYS5vYmokeAogIGluZF9zY29yZXNfcmVvcmRlcmVkIDwtIGluZF9zY29yZXNbbWF0Y2gocm93bmFtZXMoaW5kX3Njb3JlcyksIG1kYXRhX2RmW1tzYW1wbGVfaWRfY29sXV0pLCBdICU+JSAKICAgIGFzX3RpYmJsZShyb3duYW1lcyA9IHNhbXBsZV9pZF9jb2wpICU+JSAKICAgIG11dGF0ZShmaWxlbmFtZSA9IGZhY3RvcighIXN5bShzYW1wbGVfaWRfY29sKSkpCiAgaW5kX3Njb3Jlc19hbm5vdCA8LSBsZWZ0X2pvaW4oaW5kX3Njb3Jlc19yZW9yZGVyZWQsIHkgPSBtZGF0YV9kZiwgYnkgPSBzYW1wbGVfaWRfY29sKSAlPiUgCiAgc2VsZWN0KGFsbF9vZihjb2xuYW1lcyhtZGF0YV9zdWJzZXQpKSwgY29udGFpbnMoIlBDIikpCiAgcmV0dXJuKGluZF9zY29yZXNfYW5ub3QpCn0KYGBgCgoKIyMgUGVyZm9ybWluZyBQQ0Egb24gcmVndWxhciBSTUEgZGF0YQoKYGBge3J9CiMgcGNhLnJlc18xIDwtIHJlc18xICU+JSAKIyAgIGV4cHJzKCkgJT4lIAojICAgdCgpICU+JSAKIyAgIHByY29tcChjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMocGNhLnJlc18xLCAicGNhX3JlczEuUkRTIikKcGNhLnJlc18xIDwtIHJlYWRSRFMoInBjYV9yZXMxLlJEUyIpCmBgYAoKCkdldHRpbmcgdGhlIGFubm90YXRlZCBkYXRhIGZyYW1lIGZvciB0aGUgUENBLgoKYGBge3J9CnBjYS5yZXNfMS5hbm5vdF9kZiA8LSBnZXRfcGNhX2Fubm90X2RmKHBjYS5vYmogPSBwY2EucmVzXzEsIHNhbXBsZV9pZF9jb2wgPSAiZ2VvX2FjY2Vzc2lvbiIsIG1kYXRhX2RmPSBtZGF0YV9zdWJzZXQpCmhlYWQocGNhLnJlc18xLmFubm90X2RmKQpgYGAKCiMjIyBWaXN1YWxpemluZyBQQ0EgcmVzdWx0cwoKTG9va2luZyBhdCB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCAxMCBQQ3MuCgpgYGB7cn0KZnZpel9laWcocGNhLnJlc18xKSArCiAgbGFicyh4ID0gIlByaW5jaXBhbCBDb21wb25lbnQiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNjcmVlIHBsb3QgZm9yIHRoZSBmaXJzdCAxMCBwcmluY2lwYWwgY29tcG9uZW50cyBmb3IgZ2xvYmFsIFJNQS1ub3JtYWxpemVkIGRhdGEgZm9yIEdTRTc2Mjc1IiwgNjApKSArCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV93aG9sZVFOX3NjcmVlLnBuZyIsIGJnID0gIndoaXRlIikKYGBgCgpTdXBlcmltcG9zaW5nIHZhcmlhYmxlcyBpbiBkYXRhIHVwb24gc2FtcGxlIFBDQSBzY29yZXMuClRoZSBQQ0EgZG9lcyBub3Qgc2VlbSB0byBzZXBhcmF0ZSB0aGUgVE5CQyBhbmQgbm9uVE5CQyBzYW1wbGVzIHRoYXQgd2VsbCB3aGVuIHJlZ3VsYXIgUk1BIGlzIHBlcmZvcm1lZC4KCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc18xLmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSB0cmlwbGUgbmVnYXRpdmUgc3RhdHVzIGZvciBHU0U3NjI3NSBhZnRlciBnbG9iYWwgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSApCgoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0Ffd2hvbGVRTl9UTkJDX3N0YXR1cy5wbmciLCBiZyA9ICJ3aGl0ZSIpCiAgCmBgYAoKCgpUaGUgc2FtcGxlcyBkbyBub3Qgc2VlbSB0byBzZXBhcmF0ZSB3ZWxsIGJ5IHNldCBlaXRoZXIuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfMS5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gc2V0KSkgKwogIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgc2V0IChkaXNjb3Zlcnkgb3IgdmFsaWRhdGlvbikgZm9yIEdTRTc2Mjc1IGFmdGVyIGdsb2JhbCBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfYWFhcyhuYW1lID0gIlNhbXBsZSBTZXQiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV93aG9sZVFOX3NldC5wbmciLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKVGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSB0b28gc3Ryb25nIG9mIGEgYmF0Y2ggZWZmZWN0IGFjY29yZGluZyB0byBzdWJtaXNzaW9uIGRhdGUuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfMS5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gc3VibWlzc2lvbl9kYXRlKSkgKwogICAgZ2d0aXRsZSgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBcbmNvbG91cmVkIGJ5IHN1Ym1pc3Npb24gZGF0ZSBmb3Igd2hvbGUgUU4iKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV93aG9sZVFOX3NldC5wbmciKQpgYGAKCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfMS5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gdG5iY19zdWJ0eXBlKSkgKwogICAgZ2d0aXRsZSgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBcbmNvbG91cmVkIGJ5IHRuYmNfc3VidHlwZSBmb3Igd2hvbGUgUU4iKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCh4ID0gMC41LCB1bml0cyA9ICJjbSIpKSAKYGBgCgojIyBQZXJmb3JtaW5nIFBDQSBvbiBzZXBhcmF0ZWx5LXBlcmZvcm1lZCBSTUEgZGF0YQoKYGBge3J9CnBjYS5yZXNfam9pbnQgPC0gcmVzX2pvaW50ICU+JSAKICB0KCkgJT4lIAogIHByY29tcChjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMocGNhLnJlc19qb2ludCwgInBjYV9yZXNfam9pbnQuUkRTIikKcGNhLnJlc19qb2ludCA8LSByZWFkUkRTKCJwY2FfcmVzX2pvaW50LlJEUyIpCmBgYAoKCkdldHRpbmcgdGhlIGFubm90YXRlZCBkYXRhIGZyYW1lIGZvciB0aGUgUENBLgoKYGBge3J9CnBjYS5yZXNfam9pbnQuYW5ub3RfZGYgPC0gZ2V0X3BjYV9hbm5vdF9kZihwY2Eub2JqID0gcGNhLnJlc19qb2ludCwgc2FtcGxlX2lkX2NvbCA9ICJnZW9fYWNjZXNzaW9uIiwgbWRhdGFfZGY9IG1kYXRhX3N1YnNldCkKaGVhZChwY2EucmVzX2pvaW50LmFubm90X2RmKQpgYGAKCiMjIyBWaXN1YWxpemluZyBQQ0EgcmVzdWx0cwoKTG9va2luZyBhdCB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCAxMCBQQ3MuCgpgYGB7cn0KZnZpel9laWcocGNhLnJlc19qb2ludCkgKwogIGxhYnMoeCA9ICJQcmluY2lwYWwgQ29tcG9uZW50IiwgCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJTY3JlZSBwbG90IGZvciB0aGUgZmlyc3QgMTAgcHJpbmNpcGFsIGNvbXBvbmVudHMgZm9yIGNsYXNzLXNwZWNpZmljIFJNQS1ub3JtYWxpemVkIGRhdGEgZm9yIEdTRTc2Mjc1IiwgNjApKSArCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX3NjcmVlLnBuZyIpCmBgYAoKU3VwZXJpbXBvc2luZyB2YXJpYWJsZXMgaW4gZGF0YSB1cG9uIHNhbXBsZSBQQ0Egc2NvcmVzLgpUaGUgUENBICoqZG9lcyoqIHNlcGFyYXRlIHRoZSBUTkJDIGFuZCBub25UTkJDIHNhbXBsZXMgd2VsbCB3aGVuIGNsYXNzLXNwZWNpZmljIFJNQSBpcyBwZXJmb3JtZWQuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHRyaXBsZSBuZWdhdGl2ZSBzdGF0dXMgZm9yIEdTRTc2Mjc1IGFmdGVyIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9UTkJDX3N0YXR1cy5wbmciKQpgYGAKVGhlIHZhbGlkYXRpb24gbm9uVE5CQyBzYW1wbGVzIGFyZSBzZXBhcmF0ZWQgZnJvbSB0aGUgZGlzY292ZXJ5IFROQkMgYW5kIHZhbGlkYXRpb24gVE5CQyBzYW1wbGVzLgoKYGBge3J9CmdncGxvdChwY2EucmVzX2pvaW50LmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBzZXQpKSArCiAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBzZXQgKGRpc2NvdmVyeSBvciB2YWxpZGF0aW9uKSBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX2FhYXMobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX1ROQkNfc2V0LnBuZyIpCmBgYAoKU3VibWlzc2lvbiBkYXRlIGlzIHBlcmZlY3RseSBjb25mb3VuZGVkIHdpdGggVE5CQyBzdGF0dXMuIE1heSBvciBtYXkgbm90IGJlIGJhdGNoIGVmZmVjdHMuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHN1Ym1pc3Npb25fZGF0ZSkpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgc3VibWlzc2lvbiBkYXRlIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiU3VibWlzc2lvbiBEYXRlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9UTkJDX2RhdGUucG5nIikKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc19qb2ludC5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gdG5iY19zdWJ0eXBlKSkgKwogICAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBzdWJ0eXBlIG9mIFROQkMgZm9yIEdTRTc2Mjc1IGFmdGVyIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICAgc2NhbGVfY29sb3JfbmVqbShuYW1lID0gIlROQkMgU3VidHlwZSIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNSksIAogICAgICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCh4ID0gMC4zLCB1bml0cyA9ICJjbSIpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIikpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMSkpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9UTkJDX3N1YnR5cGUucG5nIikKYGBgCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGZhY3RvcihwciksIHNoYXBlID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgcHJvZ2VzdGVyb25lIHJlY2VwdG9yIHN0YXR1cyBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlByb2dlc3Rlcm9uZSBSZWNlcHRvciBTdGF0dXMiKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIsIHZhbHVlcyA9IGMoIlROIiA9IDE3LCAibm90IFROIiA9IDEpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNSksIAogICAgICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCh4ID0gMC4zLCB1bml0cyA9ICJjbSIpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIikpICsKICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAxKSkpCgoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9wcl9HU0U3NjI3NS5wbmciLCBiZz0gIndoaXRlIikKYGBgCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGZhY3RvcihoZXIyKSwgc2hhcGUgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBIRVIyIGFtcGxpZmljYXRpb24gc3RhdHVzIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbmVqbShuYW1lID0gIkhFUjIgQW1wbGlmaWNhdGlvbiBTdGF0dXMiKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIsIHZhbHVlcyA9IGMoIlROIiA9IDE3LCAibm90IFROIiA9IDEpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwgCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDEpKSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX2NsYXNzUU5faGVyMl9HU0U3NjI3NS5wbmciLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKYGBge3J9CmdncGxvdChwY2EucmVzX2pvaW50LmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBmYWN0b3IoZXIpLCBzaGFwZSA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IGVzdHJvZ2VuIHJlY2VwdG9yIHN0YXR1cyBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIkVzdHJvZ2VuIFJlY2VwdG9yIFN0YXR1cyIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIiwgdmFsdWVzID0gYygiVE4iID0gMTcsICJub3QgVE4iID0gMSkpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwgCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDEpKSkKCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX2VyX0dTRTc2Mjc1LnBuZyIpCmBgYAoKIyBQZXJmb3JtaW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCgojIyBHZXR0aW5nIGRpc3RhbmNlcwoKYGBge3J9CnBlcmZvcm1fbWluX21heCA8LSBmdW5jdGlvbih4KXsKICBtbV90cmFuc2Zvcm1hdGlvbiA8LSBwcmVQcm9jZXNzKHgsIG1ldGhvZCA9ICJyYW5nZSIpCiAgcmVzY2FsZWQgPC0gcHJlZGljdChtbV90cmFuc2Zvcm1hdGlvbiwgeCkKICByZXR1cm4ocmVzY2FsZWQpCn0KYGBgCgoKR2V0dGluZyBkaXN0YW5jZXMgYWZ0ZXIgcGVyZm9ybWluZyBtaW4gbWF4IG5vcm1hbGl6YXRpb24uCgoKYGBge3J9CiMgcmVzXzFfZGlzdHMgPC0gZXhwcnMocmVzXzEpICU+JSAKIyAgIHQoKSAlPiUgCiMgICBwZXJmb3JtX21pbl9tYXgoKSAlPiUgCiMgICBkaXN0KG1ldGhvZCA9ICJldWNsaWRlYW4iKQogIApgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKHJlc18xX2Rpc3RzLCAicmVzXzFfZGlzdHMuUkRTIikKcmVzXzFfZGlzdHMgPC0gcmVhZFJEUygicmVzXzFfZGlzdHMuUkRTIikKYGBgCgoKCmBgYHtyfQojIHJlc19qb2ludF9kaXN0cyA8LSByZXNfam9pbnQgJT4lIAojICAgICB0KCkgJT4lIAojICAgcGVyZm9ybV9taW5fbWF4KCkgJT4lIAojICAgZGlzdChtZXRob2QgPSAiZXVjbGlkZWFuIikKICAKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhyZXNfam9pbnRfZGlzdHMsICJyZXNfam9pbnRfZGlzdHMuUkRTIikKcmVzX2pvaW50X2Rpc3RzIDwtIHJlYWRSRFMoInJlc19qb2ludF9kaXN0cy5SRFMiKQpgYGAKCgoKIyMgVXNpbmcgZGVuZHJvZ3JhbXMKCmBgYHtyfQpyZXNfMV9kZW5kIDwtIHJlc18xX2Rpc3RzICU+JSAKICBoY2x1c3QoKSAlPiUgCiAgYXMuZGVuZHJvZ3JhbSgpCmBgYAoKCmBgYHtyfQpyZXNfam9pbnRfZGVuZCA8LSByZXNfam9pbnRfZGlzdHMgJT4lCiAgaGNsdXN0KCkgJT4lIAogIGFzLmRlbmRyb2dyYW0oKQpgYGAKCiAKYGBge3J9CmxpYnJhcnkoZGVuZGV4dGVuZCkKYGBgCgoKYGBge3J9CiMgcmVzXzFfZGVuZCAlPiUgCiMgICBsYWJlbHMoKQpgYGAKCgpgYGB7cn0KIyByZXNfMV9kZW5kICU+JSAKIyAgIG9yZGVyLmRlbmRyb2dyYW0oKQpgYGAKCmBgYHtyfQojIChyZXNfMSAlPiUgCiMgICBleHBycygpICU+JSAKIyAgIGNvbG5hbWVzKCkpWzI4XQogIApgYGAKCgpgYGB7cn0KcmVzXzFfZGVuZF9sYWJvcmRlciA8LSByZXNfMV9kZW5kICU+JSAKICBsYWJlbHMoKQoKYGBgCgoKYGBge3J9Cm15Y29sb3JzIDwtIGlmZWxzZShtZGF0YV9zdWJzZXRbcmVzXzFfZGVuZF9sYWJvcmRlciwgXSR0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJUTiIsICJmb3Jlc3RncmVlbiIsICJtYXJvb24iKQpgYGAKCgoKYGBge3J9CnBhcihtYXIgPSBjKDEwLDIsMSwxKSkKcmVzXzFfZGVuZCAlPiUgCiAgc2V0KCJsYWJlbHNfY2V4IiwgMC4xKSAlPiUgCiAgcGxvdCgpCgpjb2xvcmVkX2JhcnMoY29sb3JzID0gbXljb2xvcnMsIGRlbmQgPSByZXNfMV9kZW5kLCByb3dMYWJlbHMgPSAiVE4gU3RhdHVzIiwgYWRkID0gVFJVRSkKYGBgCgoKYGBge3J9CnJlc19qb2ludF9kZW5kX2xhYm9yZGVyIDwtIHJlc19qb2ludF9kZW5kICU+JSAKICBsYWJlbHMoKQpgYGAKCgpgYGB7cn0KbXljb2xvcnMgPC0gaWZlbHNlKG1kYXRhX3N1YnNldFtyZXNfam9pbnRfZGVuZF9sYWJvcmRlciwgXSR0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJUTiIsICJmb3Jlc3RncmVlbiIsICJtYXJvb24iKQpgYGAKCgoKYGBge3J9CnBhcihtYXIgPSBjKDEwLDIsMSwxKSkKcmVzX2pvaW50X2RlbmQgJT4lIAogIHNldCgibGFiZWxzX2NleCIsIDAuMSkgJT4lIAogIHBsb3QoKQoKY29sb3JlZF9iYXJzKGNvbG9ycyA9IG15Y29sb3JzLCBkZW5kID0gcmVzX2pvaW50X2RlbmQsIHJvd0xhYmVscyA9ICJUTiBTdGF0dXMiLCBhZGQgPSBUUlVFKQpgYGAKCgojIyBVc2luZyBoZWF0bWFwcwoKRnVuY3Rpb24gdG8gcHJvY2VzcyBkaXN0YW5jZSBvYmplY3QgaW50byBhIGRpc3RhbmNlIG1hdHJpeCBmb3IgaGVhdG1hcCB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmdldF9kaXN0bWF0IDwtIGZ1bmN0aW9uKHgpewogIGRpc3RtYXQgPC0gYXMubWF0cml4KHgpCiAgY29sbmFtZXMoZGlzdG1hdCkgPC0gTlVMTAogIGRpYWcoZGlzdG1hdCkgPC0gTkEKICByZXR1cm4oZGlzdG1hdCkKfQpgYGAKCgoKYGBge3J9CnJvd19hbm5vdCA8LSBtZGF0YV9zdWJzZXQgJT4lIAogIG11dGF0ZShlciA9IGZhY3RvcihlciksIHByID0gZmFjdG9yKHByKSwgaGVyMiA9IGZhY3RvcihoZXIyKSkgJT4lIAogIHNlbGVjdChzdWJtaXNzaW9uX2RhdGUsIHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMsIHByLCBlciwgaGVyMikgJT4lIAogIHJlbmFtZShgVE4gc3RhdHVzYCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpCgpoZWFkKHJvd19hbm5vdCkKYGBgCgoKYGBge3J9CnNldC5zZWVkKDUpCnJvd19jb2xvdXJzIDwtIGxpc3QoICJUTiBzdGF0dXMiID0gYygiZGFya2dvbGRlbnJvZDQiLCAiZGFya21hZ2VudGEiKSwgCiAgICAgICAgICAgICAgICAgICAgICJzdWJtaXNzaW9uX2RhdGUiID0gcGFsX25lam0oKSgyKSwKICAgICAgICAgICAgICAgICAgICAgInByIiA9IHBhbF9sYW5jZXQoKSg5KVsxOjJdLCAKICAgICAgICAgICAgICAgICAgICAgImVyIiA9IHBhbF9sYW5jZXQoKSg5KVs4OjldLAogICAgICAgICAgICAgICAgICAgICAiaGVyMiIgPSBwYWxfbGFuY2V0KCkoOSlbNTo3XSkKCm5hbWVzKHJvd19jb2xvdXJzW1siVE4gc3RhdHVzIl1dKSA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHJvd19hbm5vdFtbIlROIHN0YXR1cyJdXSkpCm5hbWVzKHJvd19jb2xvdXJzJHByKSA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHJvd19hbm5vdCRwcikpCm5hbWVzKHJvd19jb2xvdXJzJGVyKSA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHJvd19hbm5vdCRlcikpCm5hbWVzKHJvd19jb2xvdXJzJGhlcjIpIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90JGhlcjIpKQpuYW1lcyhyb3dfY29sb3VycyRzdWJtaXNzaW9uX2RhdGUpIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90JHN1Ym1pc3Npb25fZGF0ZSkpCnN0cihyb3dfY29sb3VycykKYGBgCmBgYHtyfQpteV9jb2xvdXJzIDwtICB2aXJpZGlzKDI2NV4yLCBiZWdpbiA9IDEsIGVuZCA9IDApCmBgYAoKYGBge3J9CnJlc19qb2ludF9kaXN0cyAlPiUgCiAgZ2V0X2Rpc3RtYXQoKSAlPiUgCnBoZWF0bWFwKC4sCiAgICAgICAgIGNvbG9yID0gbXlfY29sb3VycywKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSByb3dfYW5ub3QsCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gcm93X2NvbG91cnMsCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICBzaG93X3Jvd25hbWVzID0gRiwKICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLAogICAgICAgICBjdXRyZWVfY29scyA9IDIsCiAgICAgICAgIG1haW4gPSBzdHJfd3JhcCgiSGVhdG1hcCBvZiBzYW1wbGUgZGlzdGFuY2VzIGZvciBjbGFzcy1zcGVjaWZpYyBRTiBleHByZXNzaW9uIG1hdHJpeCBmb3IgR1NFNzYyNzUiLCA2MCksCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKCJzbWFsbCBkaXN0YW5jZSIsICJsYXJnZSBkaXN0YW5jZSIpLAogICAgICAgICBsZWdlbmRfYnJlYWtzID0gYyhtaW4oLiwgbmEucm0gPSBUUlVFKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoLiwgbmEucm0gPSBUUlVFKSksIAogICAgICAgICAgZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvY2xhc3NRTl9jbHVzdGVyaW5nX2hlYXRtYXBfR1NFNzYyNzUucG5nIikKYGBgCgoKIyBQZXJmb3JtaW5nIFNWQQoKIyMgUGVyZm9ybWluZyBTVkEgb24gcmVndWxhciBSTUEgZGF0YQoKCmBgYHtyfQpmdWxsX21vZCA8LSBtZGF0YV9zdWJzZXQgJT4lIAogIHNlbGVjdChnZW9fYWNjZXNzaW9uLCB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSAlPiUgCiAgYXJyYW5nZSh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSAlPiUgCiAgbW9kZWwubWF0cml4KH50cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBkYXRhID0gLikKCmhlYWQoZnVsbF9tb2QpCmBgYAoKCmBgYHtyfQpyZWRfbW9kIDwtIG1vZGVsLm1hdHJpeCh+MSwgZGF0YSA9IG1kYXRhX3N1YnNldCkKCmhlYWQocmVkX21vZCkKYGBgCgpHZXQgbnVtYmVyIG9mIHNpZ25pZmljYW50IHN1cnJvZ2F0ZSB2YXJpYWJsZXMuCgpgYGB7cn0Kbi5zdi53aG9sZVFOIDwtIG51bS5zdihleHBycyhyZXNfMSksIGZ1bGxfbW9kLCBtZXRob2Q9ImxlZWsiKQpgYGAKCgpgYGB7cn0Kbi5zdi53aG9sZVFOCmBgYAoKCmBgYHtyfQpzdm9iai53aG9sZVFOIDwtIHN2YShleHBycyhyZXNfMSksIG1vZCA9IGZ1bGxfbW9kLCBtb2QwID0gcmVkX21vZCwgbi5zdiA9IDEpCmBgYAoKCmBgYHtyfQpzdl9kZi53aG9sZVFOIDwtIHRpYmJsZSgiZ2VvX2FjY2Vzc2lvbiIgPSBjb2xuYW1lcyhleHBycyhyZXNfMSkpLCAic3YiID0gc3ZvYmoud2hvbGVRTiRzdikKCmhlYWQoc3ZfZGYud2hvbGVRTikKYGBgCgoKCmBgYHtyfQpsZWZ0X2pvaW4oc3ZfZGYud2hvbGVRTiwgbWRhdGEsIGJ5ID0gImdlb19hY2Nlc3Npb24iKSAlPiUgCiAgbXV0YXRlKGluZGV4ID0gNSkgJT4lIAogIGdncGxvdCgpICsKICAjIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeSA9IGZjdF9yZW9yZGVyKGdlb19hY2Nlc3Npb24sIHN2LCAuZnVuID0gZnVuY3Rpb24oeCl7eH0pLCB4ID0gc3YsIGZpbGwgPSBzZXQpKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHN1Ym1pc3Npb25fZGF0ZSwgeSA9IHN2LCBmaWxsID0gc2V0KSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGxhYnMoeSA9ICJTdXJyb2dhdGUgVmFyaWFibGUgVmFsdWUiLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgbGF0ZW50IHZhcmlhYmxlIGVzdGltYXRlZCBieSBTVkEgZm9yIGRpZmZlcmVudCBncm91cGluZyBmYWN0b3JzIikKICAKCiMgZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9zdmFfZ3JvdXBpbmdfbm9ybWFsUk1BLnBuZyIpCmBgYAoKCiMjIFBlcmZvcm1pbmcgU1ZBIG9uIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6ZWQgZGF0YQoKQ3JlYXRlIGZ1bGwgbW9kZWwgbWF0cml4LgoKYGBge3J9CmZ1bGxfbW9kIDwtIG1kYXRhX3N1YnNldCAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24sIHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpICU+JSAKICBhcnJhbmdlKHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpICU+JSAKICBtb2RlbC5tYXRyaXgofnRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMsIGRhdGEgPSAuKQoKaGVhZChmdWxsX21vZCkKYGBgCgpDcmVhdGUgcmVkdWNlZCBtb2RlbCBtYXRyaXguCgpgYGB7cn0KcmVkX21vZCA8LSBtb2RlbC5tYXRyaXgofjEsIGRhdGEgPSBtZGF0YV9zdWJzZXQpCgpoZWFkKHJlZF9tb2QpCmBgYAoKR2V0IG51bWJlciBvZiBzaWduaWZpY2FudCBzdXJyb2dhdGUgdmFyaWFibGVzLgoKYGBge3J9Cm4uc3YuY2xhc3NRTiA8LSBudW0uc3YocmVzX2pvaW50LCBmdWxsX21vZCwgbWV0aG9kPSJsZWVrIikKYGBgCgoKYGBge3J9Cm4uc3YuY2xhc3NRTgpgYGAKCgpQZXJmb3JtIFNWQSBvbiBjbGFzc1FOLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBtYXRyaXguCgpgYGB7cn0Kc3ZvYmouY2xhc3NRTiA8LSBzdmEocmVzX2pvaW50LCBtb2QgPSBmdWxsX21vZCwgbW9kMCA9IHJlZF9tb2QsIG4uc3YgPSBuLnN2LmNsYXNzUU4pCmBgYAoKCmBgYHtyfQpzdl9kZi5jbGFzc1FOIDwtIHRpYmJsZSgiZ2VvX2FjY2Vzc2lvbiIgPSBjb2xuYW1lcyhyZXNfam9pbnQpLCAic3YiID0gc3ZvYmouY2xhc3NRTiRzdikKCmhlYWQoc3ZfZGYuY2xhc3NRTikKYGBgCgpgYGB7cn0Kc2F2ZVJEUyhzdl9kZi5jbGFzc1FOLCAic3ZfZGZfY2xhc3NRTi5SRFMiKQojIHN2X2RmLmNsYXNzUU4gPC0gcmVhZFJEUygic3ZfZGZfY2xhc3NRTi5SRFMiKQpgYGAKCgpgYGB7cn0Kc3ZfZGYuY2xhc3NRTiA8LSBsZWZ0X2pvaW4oc3ZfZGYuY2xhc3NRTiAsIG1kYXRhLCBieSA9ICJnZW9fYWNjZXNzaW9uIikKYGBgCgoKYGBge3J9CnN2X2RmLmNsYXNzUU4gJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHN1Ym1pc3Npb25fZGF0ZSwgeSA9IHN2LCBmaWxsID0gZXIpKSArCiAgIyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHN1Ym1pc3Npb25fZGF0ZSwgeSA9IHN2LCBjb2xvciA9IGVyKSkgKwogIHRoZW1lX2xpZ2h0KCkgCiAgIyBsYWJzKHkgPSAiU3Vycm9nYXRlIFZhcmlhYmxlIFZhbHVlIiwgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGxhdGVudCB2YXJpYWJsZSBlc3RpbWF0ZWQgYnkgU1ZBIGZvciBkaWZmZXJlbnQgZ3JvdXBpbmcgZmFjdG9ycyIpCiAgCgojIGdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvc3ZhX2dyb3VwaW5nX2NsYXNzUU4ucG5nIikKYGBgCgoKIyBUcnlpbmcgdG8gc2VlIGlmIHRoZSBTVkEgZXN0aW1hdGVzIGEgYmF0Y2ggd2hlbiBRTiBpcyBub3QgYXBwbGllZAoKSW4gdGhpcyBhdHRlbXB0LCBJIHBlcmZvcm0gbm8gcXVhbnRpbGUgbm9ybWFsaXphdGlvbiB3aGlsZSBwZXJmb3JtaW5nIFJNQS4gSWYgUU4gaGFzIG5vdCBiZWVuIHBlcmZvcm1lZCBhbmQgYSBzdXJyb2dhdGUgdmFyaWFibGUgc2hvd3MgdXAgdGhhdCBjb3JyZXNwb25kcyB0byBiYXRjaCwgYmF0Y2ggZWZmZWN0cyBhcmUgcHJvYmFibHkgcHJlc2VudC4KCmBgYHtyfQpyYXdEYXRhLnN1bW1hcnkgPC0gcm1hKHJhd0RhdGEsIGJhY2tncm91bmQgPSBUUlVFLCBub3JtYWxpemUgPSBGQUxTRSkKYGBgCgoKYGBge3J9CnJhd0RhdGEuc3VtbWFyeV9kZl9sb25nIDwtIHJhd0RhdGEuc3VtbWFyeSAlPiUgCiAgZXhwcnMoKSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInByb2JlSUQiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YoYyh0bmJjX3NhbXBsZXMsIG5vbnRuYmNfc2FtcGxlcykpLCBuYW1lc190byA9ICJzYW1wbGVfaWQiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBsZWZ0X2pvaW4oLiwgbWRhdGFfc3Vic2V0LCBieSA9IGMoInNhbXBsZV9pZCIgPSAiZ2VvX2FjY2Vzc2lvbiIpKQpgYGAKCgoKYGBge3J9CnAzIDwtIHJhd0RhdGEuc3VtbWFyeV9kZl9sb25nICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoc2FtcGxlX2lkLCBhcy5udW1lcmljKHNldCkpLCB5ID0gaW50ZW5zaXR5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHNldCkpICsKICBsYWJzKHggPSAic2FtcGxlcyIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2FtcGxlLXdpc2UgbG9nMiBpbnRlbnNpdHkgYm94cGxvdHMgaW4gdGhlIGFic2VuY2Ugb2YgUU4iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCgpwMwpgYGAKCgpgYGB7cn0KZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9ub1FOX2JveHBsb3RzLnBuZyIsIAogICAgICAgcDMsIAogICAgICAgdW5pdHMgPSAiY20iLCB3aWR0aCA9IDMwLCBoZWlnaHQgPSAxMCkKYGBgCgpHZXR0aW5nIHRoZSBudW1iZXIgb2Ygc3Vycm9nYXRlIHZhcmlhYmxlcyBpbiB0aGUgYWJzZW5jZSBvZiBxdWFudGlsZSBub3JtYWxpemF0aW9uLgoKYGBge3J9Cm4uc3Yubm9ub3JtIDwtIG51bS5zdihleHBycyhyYXdEYXRhLnN1bW1hcnkpLCBmdWxsX21vZCwgbWV0aG9kPSJsZWVrIikKYGBgCgpUaGVyZSBpcyBvbmUgc3Vycm9nYXRlIHZhcmlhYmxlIHByZXNlbnQgaW4gdGhlIGFic2VuY2Ugb2YgUU4uCgpgYGB7cn0Kbi5zdi5ub25vcm0KYGBgCgoKYGBge3J9CnN2b2JqLm5vbm9ybSA8LSBzdmEoZXhwcnMocmF3RGF0YS5zdW1tYXJ5KSwgbW9kID0gZnVsbF9tb2QsIG1vZDAgPSByZWRfbW9kLCBuLnN2ID0gbi5zdi5ub25vcm0pCmBgYAoKCmBgYHtyfQpzdl9kZi5ub25vcm0gPC0gdGliYmxlKCJnZW9fYWNjZXNzaW9uIiA9IGNvbG5hbWVzKGV4cHJzKHJhd0RhdGEuc3VtbWFyeSkpLCAic3YiID0gc3ZvYmoubm9ub3JtJHN2KQoKaGVhZChzdl9kZi5ub25vcm0pCmBgYAoKCmBgYHtyfQpsZWZ0X2pvaW4oc3ZfZGYubm9ub3JtLCBtZGF0YSwgYnkgPSAiZ2VvX2FjY2Vzc2lvbiIpICU+JSAKICBtdXRhdGUoaW5kZXggPSA1KSAlPiUgCiAgZ2dwbG90KCkgKwogICMgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh5ID0gZmN0X3Jlb3JkZXIoZ2VvX2FjY2Vzc2lvbiwgc3YsIC5mdW4gPSBmdW5jdGlvbih4KXt4fSksIHggPSBzdiwgZmlsbCA9IHNldCkpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gc3VibWlzc2lvbl9kYXRlLCB5ID0gc3YsIGZpbGwgPSBzZXQpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgbGFicyh5ID0gIlN1cnJvZ2F0ZSBWYXJpYWJsZSBWYWx1ZSIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiRGlzdHJpYnV0aW9uIG9mIGxhdGVudCB2YXJpYWJsZSBlc3RpbWF0ZWQgYnkgU1ZBIGZvciBkaWZmZXJlbnQgZ3JvdXBpbmcgZmFjdG9ycyIsIDYwKSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvc3ZhX2dyb3VwaW5nX25vUU4ucG5nIikKYGBgCgoKYGBge3J9CgpgYGAKCg==